
線程狀態(tài)轉(zhuǎn)換圖.png
問題:請使用兩個線程一個線程用來打印1-26,另一個線程依次打印A-Z,要求交叉打印。
//1.使用wait和notify方法
public class WaitAndNotify {
static Thread t1=null,t2=null;
private static volatile boolean t1HasPrint=false;//標(biāo)記信號量
public static void main(String[] args) {
Object lock=new Object();
t1=new Thread(()->{
for (char i='A';i<='Z';i++){
synchronized (lock){
while (t1HasPrint){
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.print(i);
t1HasPrint=true;
lock.notify();
}
}
},"t1");
t2=new Thread(()->{
for (int i=1;i<=26;i++){
synchronized (lock){
while (!t1HasPrint){
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.print(i);
t1HasPrint=false;
lock.notify();
}
}
},"t2");
t2.start();
t1.start();
}
}
//2.使用LockSupport
public class TestLockSupport {
static Thread t1=null,t2=null;
private static volatile boolean t1HasPrint=false;
public static void main(String[] args) {
t1=new Thread(()->{
for (char i='A';i<='Z';i++){
while (t1HasPrint){
LockSupport.park();
}
System.out.print(i);
t1HasPrint=true;
LockSupport.unpark(t2);
}
},"t1");
t2=new Thread(()->{
for (int i=1;i<=26;i++){
while (!t1HasPrint){
LockSupport.park();
}
System.out.print(i);
t1HasPrint=false;
LockSupport.unpark(t1);
}
},"t2");
t2.start();
t1.start();
}
}
總結(jié):
- wait()、notify/notifyAll() 方法是Object的本地final方法,無法被重寫。
- 一般在synchronized同步代碼塊里使用wait()、notify()和notifyAll()方法。
- 當(dāng)線程執(zhí)行wait()方法時(shí),會釋放當(dāng)前的鎖,然后讓出CPU,進(jìn)入等待隊(duì)列。(所以使用wait()的前提的先獲得鎖)
- 當(dāng)線程t執(zhí)行notify或notifyAll()方法后,會喚醒一個或全部正處于等待狀態(tài)的線程,但是線程t不會立即釋放鎖,直到執(zhí)行完同步代碼塊的代碼或者遇到wait()方法,才會釋放鎖。所以被喚醒的鎖也不會立即獲得鎖。
- wait() 需要被try catch包圍,以便發(fā)生異常中斷也可以使wait等待的線程喚醒。
- notify喚醒沉睡的線程后,線程會接著上次的執(zhí)行繼續(xù)往下執(zhí)行。
- 永遠(yuǎn)在循環(huán)里調(diào)用wait()和notify(),而不是在if語句。
- notify方法只喚醒一個等待線程。notifyAll 會喚醒所有等待線程,盡管哪一個線程將會第一個處理取決于操作系統(tǒng)。