承上啓下
上篇博客介紹了現在JDK棄用的api,這篇博客介紹現在使用的兩種api
wait/notify機制
這些方法只能由(同一對象鎖)的持有者線程調用,也就是卸載同步塊裏面,否則會拋出IllegalMonitorStateException異常。一定要在同步代碼段
wait方法導致當前線程等待,加入該對象的等待集合中,並且放棄當前持有的對象鎖,notify/notifyAll方法喚醒一個或所有的正在等待這個對象鎖的線程
package demo2;
public class wait {
public static Object baozidian=null;
public void waitNotifyTest() throws Exception{
//啓動線程
new Thread(()->{
if(baozidian==null){ //如果沒有包子,則進入等待
synchronized (this){
try{
System.out.println("1.進入等待");
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
System.out.println("2.買到包子,回家");
}).start();
//3秒之後,生產一個包子
Thread.sleep(3000L);
baozidian=new Object();
synchronized (this){
this.notifyAll();
System.out.println("3.通知消費者");
}
}
public static void main(String[] args) throws Exception {
new wait().waitNotifyTest();
}
}
產生死鎖情況
注意,雖然會wait自動解鎖,但是對順序有要求,如果在notify被調用之後,纔開始wait方法的調用,線程會永遠處理waiting狀態
package demo2;
public class wait {
public static Object baozidian=null;
public void waitNotifyTest() throws Exception{
//啓動線程
new Thread(()->{
if(baozidian==null){ //如果沒有包子,則進入等待
try{
Thread.sleep(5000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (this){
try{
System.out.println("1.進入等待");
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
System.out.println("2.買到包子,回家");
}).start();
//3秒之後,生產一個包子
Thread.sleep(3000L);
baozidian=new Object();
synchronized (this){
this.notifyAll(); //喚醒所有的線程
System.out.println("3.通知消費者");
}
}
public static void main(String[] args) throws Exception {
new wait().waitNotifyTest();
}
}
3秒之後在notify被調用,不過這時候wait方法沒有被調用,5秒之後在進行被調用,所以線程會永遠處理waiting狀態
park/unpark機制
線程調用park則等待"許可",unpark方法爲指定線程提供"許可(permit)"
不要求park和unpark的調用順序
多次調用unpark之後,在調用park,線程會直接運行,但不會疊加,也就是說,連續多次調用park方法,第一次會拿到"許可"直接運行,後續調用會進入等待
package demo2;
import java.util.concurrent.locks.LockSupport;
public class park {
public static Object baozidian=null;
public void parkUnparkTest() throws Exception{
try{ //喚醒的程序已經被執行過了
Thread.sleep(5000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
//啓動線程
Thread consumerThread=new Thread(()->{
if(baozidian==null){
System.out.println("1.進入等待");
LockSupport.park();
}
System.out.println("2.買到包子,回家");
});
consumerThread.start();
//3秒之後,生產一個包子
Thread.sleep(3000L);
baozidian=new Object();
LockSupport.unpark(consumerThread);
System.out.println("3.通知消費者");
}
public static void main(String[] args) throws Exception {
new park().parkUnparkTest();
}
}
產生死鎖情況
不會對鎖進行釋放,只是操作線程
package demo2;
import java.util.concurrent.locks.LockSupport;
public class park {
public static Object baozidian=null;
public void parkUnparkTest() throws Exception{
try{ //喚醒的程序已經被執行過了
Thread.sleep(5000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
//啓動線程
Thread consumerThread=new Thread(()->{
if(baozidian==null){
System.out.println("1.進入等待");
// 當前線程拿到鎖,然後掛起
synchronized (this){ //沒有被釋放,一直處理被掛起的狀態
LockSupport.park();
}
}
System.out.println("2.買到包子,回家");
});
consumerThread.start();
//3秒之後,生產一個包子
Thread.sleep(3000L);
baozidian=new Object();
//爭取到鎖以後,在恢復consumerThread
synchronized (this){
LockSupport.unpark(consumerThread);
}
System.out.println("3.通知消費者");
}
public static void main(String[] args) throws Exception {
new park().parkUnparkTest();
}
}
警告!之前代碼中用if語句來判斷,是否進行等待狀態,是錯誤的
官方建議應該在while循環中檢查等待條件,原因是處於等待狀態的線程可能會收到錯誤警報和僞喚醒,如果不在循環中檢查等待條件,程序就會在沒有滿足結束條件的情況下推出。僞喚醒是指線程並非因爲notify,notifyall,unpark等api調用而喚醒,是更底層原因導致的
今天就先分享到這裏,之後還有一系列的課