問題描述
有兩個線程,一個線程隻列印奇數,一個線程隻列印偶數,而且要按照順序列印,就是說要按照這樣的方式列印:
偶線程:0
奇線程:1
偶線程:2
奇線程:3
…
方案一:使用synchronized關鍵字
要點:
- 建立兩個線程,一個線程處理偶數,一個線程處理奇數
- 兩個線程之間通過synchronized進行同步,保證count++每次隻有一個線程進行操作
- 為什麼兩個線程能交替執行,這裡很巧的是
,實際上兩個線程都是在不停的嘗試(while循環)進入synchronized代碼塊,如果滿足相對應的條件(偶數或是奇數)就列印輸出。count從0123自增過程就是一個奇偶數交替的過程
public class WaitNotifyPrintOddEvenSyn {
private static int count;
private static final Object lock = new Object();
/**
* 建立2個線程,第一個隻處理偶數,第二個隻處理奇數(用位運算);用synchronized來通信
*/
public static void main(String[] args) {
new Thread(() -> {
while (count < 100) {
synchronized (lock) {
if ((count & 1) == 0) {
System.out.println(Thread.currentThread().getName() + ":" + count++);
}
}
}
}, "偶線程").start();
new Thread(() -> {
while (count < 100) {
synchronized (lock) {
if ((count & 1) == 1) {
System.out.println(Thread.currentThread().getName() + ":" + count++);
}
}
}
}, "奇線程").start();
}
}
方案二:使用wait/notify關鍵字(推薦)
要點:
- 這個相對于上面那個好了解很多,直接走流程,偶數線程拿到鎖列印輸出同時count++,然後進行休眠,因為
,奇數線程就可以進來了,進來後列印輸出,同時notify喚醒偶數線程繼續下一輪,奇數線程往下執行wait方法休眠,就這樣,偶數線程喚醒奇數線程,奇數線程喚醒偶數線程,直到滿足count<100條件後,線程不再休眠,直接退出程式。wait()方法的特性,休眠的同時會釋放monitor鎖
- 這個要點一個在于
,一個在于wait()方法的特性,休眠後會釋放鎖。wait/notify的等待喚醒機制
- 這種方式和上面那種方式不同點在于,這種方式是
,而上面那個是線程不斷重試的機制(一直while重試,直到滿足條件就列印),很明顯這種方式優于上面那種!被動喚醒的機制
public class WaitNotifyPrintOddEveWait {
private static int count = 0;
private static final Object lock = new Object();
public static void main(String[] args) {
new Thread(new TurningRunner(), "偶線程").start();
new Thread(new TurningRunner(), "奇線程").start();
}
/**
* 1. 拿到鎖,立刻列印
* 2. 列印完,喚醒其他線程,自己就休眠
*/
static class TurningRunner implements Runnable {
@Override
public void run() {
while (count < 100) {
synchronized (lock) {
//拿到鎖就列印
System.out.println(Thread.currentThread().getName() + ":" + count++);
lock.notify();
if (count < 100) {
try {
//如果任務還沒結束,就讓出目前的鎖,并休眠
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}
}
方案三:可以使用ReentrantLock,設定為公平鎖,即等待時間越長的線程先執行
筆記來源:慕課網悟空老師視訊《Java并發核心知識體系精講》