戳藍字「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】閱讀更多精彩好文。