一起聊聊3個線程依次打印1、2、3...的故事

 戳藍字「TopCoder」關注我們哦!

3個線程依次打印1、2、3…這個問題,常常被作爲面試題,題目如下:

三個線程,一個線程負責打印1,4,7,……;第二個負責打印2,5,8,……,第三個負責打印3,6,9,……,要求在控制檯中按順序輸出1,2,3,4,5,6……。

這個題目肯定是要啓動3個線程的,那怎麼讓這3個線程“協作”按順序打印1、2、3呢?從大的方面來講,這種“協作”可分爲以下兩種:

  • 競爭型:每個線程都搶着去打印,如果發現不該自己打印,則準備下一輪搶。由於大家都是競爭的,因此需要用鎖機制來保護。

  • 協同型:當前線程線程打印之後通知下一個線程去打印,這種需要確認好第一個線程打印時機。由於是協同型的因此可以不用鎖機制來保護,但是需要一個通知機制。

競爭型打印

多個線程競爭型打印,優勢是代碼簡單易懂,劣勢是線程爭搶是CPU調度進行的,可能該某個線程打印時結果該線程遲遲未被CPU調度,結果其他線程被CPU調度到但是由於不能執行打印操作而繼續爭搶,造成CPU性能浪費。示例代碼如下:

public class DemoTask implements Runnable {

    // 這裏將lock對象換成 Lock(ReentrantLock) 進行lock/unlock也是可以的
    private static final Object lock = new Object();
    private static final int MAX = 1024;
    private static int current = 1;

    private int index;
    public DemoTask(int index) {
        this.index = index;
    }

    @Override
    public void run() {
        while (current <= MAX) {
            synchronized (lock) {
                if ((current <= MAX) && (current % 3 == index)) {
                    System.out.println(current++);
                }
            }
        }
    }

    public static void main(String[] args) throws Exception {
        List<Thread> threadList = Arrays.asList(
                new Thread(new DemoTask(0)),
                new Thread(new DemoTask(1)),
                new Thread(new DemoTask(2))
        );

        threadList.forEach(Thread::start);
        for (Thread thread : threadList) {
            thread.join();
        }
    }
}

協同型打印

多個線程協同型打印,優勢是各個線程使用“通知”機制進行協同分工,理論上執行效率較高,不過要使用對應的“通知”機制。關於如何“通知”,第一種是可使用Java對象的 wait/notify 或者Conditon對象的 await/signal ,第二種是以事件或者提交任務的方式(比如通過提交“待打印數字”這個任務給下一個線程)。

下面以第二種方式進行代碼分析,比如當前線程通過submit給下一個線程一個“待打印數字”的任務,這樣很容易想到使用只包含1個線程的線程池來實現,示例代碼如下:

public class DemoTask implements Runnable {

    // 主線程要等待線程打印完畢,使用CountDownLatch通知機制
    private static CountDownLatch countDownLatch = new CountDownLatch(1);
    private static List<ExecutorService> threadList = new ArrayList<>();
    private static final int MAX = 1024;
    private static int current = 1;

    @Override
    public void run() {
        if (current <= MAX) {
            System.out.println(current++);
            threadList.get(current % threadList.size()).submit(new DemoTask());
        } else {
            countDownLatch.countDown();
        }
    }

    public static void main(String[] args) throws InterruptedException {
        for (int i = 0; i < 3; i++) {
            threadList.add(Executors.newFixedThreadPool(1));
        }

        threadList.get(0).submit(new DemoTask());
        countDownLatch.await();
        threadList.forEach(ExecutorService::shutdown);
    }
}

 推薦閱讀 


歡迎小夥伴關注【TopCoder】閱讀更多精彩好文。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章