重量級鎖通過對象內部的監視器(monitor)實現,其中monitor的本質是依賴於底層操作系統的Mutex Lock實
現,操作系統實現線程之間的切換需要從用戶態到內核態的切換,切換成本非常高。前面我們在講Java對象頭的時候,講到了monitor這個對象,在hotspot虛擬機中,通過ObjectMonitor類來實現monitor。他的鎖的獲取過程的體現會簡單很多.
wait 和notify
wait和notify是用來讓線程進入等待狀態以及使得線程喚醒的兩個操作
wait()必須被synchronized來使用,
public class ThreadWait extends Thread{
private Object lock;
public ThreadWait(Object lock) {
this.lock = lock;
}
@Override
public void run() {
synchronized (lock){
System.out.println("開始執行 thread wait");
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("執行結束 thread wait");
}
}
}
public class ThreadNotify(Object lock) {
this.lock = lock;
}
@Override
public void run() {
synchronized (lock){
System.out.println("開始執行 thread notify");
lock.notify();
System.out.println("執行結束 thread notify");
}
}
}
wait 和notify的原理
- 調用wait() 首先會獲取監視器鎖,獲得成功後,會讓線程進入等待狀態進入等待隊列並且釋放鎖;
- 然後當其他線程調用notify或者notifyall以後,會選擇從等待隊列中喚醒任意一個線程
- 而執行完notify方法以後,並不會立馬喚醒線程,原因是當前線程仍然持有這把鎖,處於等待狀態的線程無法獲得鎖。必須要等到當前的線程執行完按monitorexit指令之後,也就是被釋放之後,處於等待隊列的線程就可以開始競爭鎖了。
wait和notify爲什麼要放在synchronized裏面
wait方法的語義有兩個,
- 釋放當前的對象鎖、
- 使得當前線程進入阻塞隊列,
而這些操作都和監視器是相關的,所以wait必須要獲得一個監視器鎖。
notify也一樣,它是喚醒一個線程,所以需要知道待喚醒的線程在哪裏,就必須找到這個對象獲取這個對象的鎖然後去到這個對象的等待隊列去喚醒一個線程。