一起聊聊3個執行緒依次列印1、2、3...的故事
阿新 • • 發佈:2020-02-29
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); } }
推薦閱讀
- 資料序列化的那些事
- kubernetes:Pod基礎概念知多少
- kubernetes基礎概念知多少