進入正題前須要瞭解的概念
- 初始(NEW):新建立了一個線程對象,但尚未調用start()方法。
- 運行(RUNNABLE):Java線程準備就緒(ready)和運行中(running)兩種狀態籠統的成爲「運行」。
線程對象建立後,其餘線程(好比main線程)調用了該對象的start()方法。該狀態的線程位於可運行線程池中,等待被線程調度選中,獲取cpu 的使用權,此時處於就緒狀態(ready)。就緒狀態的線程在得到cpu 時間片後變爲運行中狀態(running)。
- 阻塞(BLOCKED):表線程阻塞於鎖。
- 等待(WAITING):進入該狀態的線程須要等待其餘線程作出一些特定動做(通知或中斷)。
- 超時等待(TIME_WAITING):該狀態不一樣於WAITING,它能夠在指定的時間內自行返回。
- 終止(TERMINATED):表示該線程已經執行完畢。
- JVM爲每個對象和類都關聯一個鎖,鎖住了一個對象,就是得到對象相關聯的監視器。
- 當聲明 synchronized 的代碼塊時,編譯而成的字節碼會包含一個監視器和多個監視器退出路徑 (多個退出路徑,正常和異常狀況),當執行被鎖的代碼時,檢查目標鎖對象的計數器是否爲0,若是爲0則將鎖對象的持有線程設置爲本身,而後計數器加1,獲取到鎖,若是不爲0則檢查鎖對象的持有線程是否是本身,若是是本身就將計數器加1獲取鎖,若是不是則阻塞等待,退出的時候計數器減1,當減爲0的時候清楚鎖對象的持有線程標記。(能夠看出 synchronized 是支持可重入的)
- 邏輯上鎖是對象內存堆中頭部的一部分數據。JVM中的每一個對象都有一個鎖,任何程序均可以使用它來協調對對象的多線程訪問。若是任何線程想要訪問該對象的實例變量,那麼線程必須擁有該對象的鎖(在鎖內存區域設置的標誌)。全部其餘的線程試圖訪問該對象的變量,必須等到擁有該對象的鎖有的線程釋放鎖(改變標記)。
- 監視器是任何一個對象都有的內置的同步數據結構,它是用來協調有多個線程調用當前對象的時的執行順序,線程會block或者wait 在一個對象的監視器上,可理解爲監視器確保只有一個線程訪問共享資源,所有期待得到鎖的線程,在鎖已經被其它線程擁有的時候,這些期待得到鎖的線程就進入了監視器的entry set區域。全部曾經得到過鎖,可是因爲其它必要條件不知足而須要wait的時候,線程就進入了監視器的wait set區域 。在wait set區域的線程得到Notify/notifyAll通知的時候,隨機的一個Thread(Notify)或者是所有的Thread(NotifyALL)從監視器的wait set區域進入了entry set中。在當前擁有鎖的線程釋放掉鎖的時候,處於該監視器的entryset區域的線程都會搶佔該鎖,可是隻能有任意的一個Thread能取得該鎖,而其餘線程依然在entry set中等待下次來搶佔到鎖以後再執行。
- 因此,監視器實際上就是實現synchronized鎖的具體實現方法
-
jdk文檔中的描述
- wait:當前線程必須擁有此對象監視器。該線程發佈對此監視器的全部權並等待,直到其餘線程經過調用
notify
方法,或 notifyAll
方法通知在此對象的監視器上等待的線程醒來。而後該線程將等到從新得到對監視器的全部權後才能繼續執行。
- notify:喚醒在此對象監視器上等待的單個線程。若是全部線程都在此對象上等待,則會選擇喚醒其中一個線程。選擇是任意性的,並在對實現作出決定時發生。線程經過調用其中一個
wait
方法,在對象的監視器上等待。直到當前線程放棄此對象上的鎖定,才能繼續執行被喚醒的線程。被喚醒的線程將以常規方式與在該對象上主動同步的其餘全部線程進行競爭;例如,喚醒的線程在做爲鎖定此對象的下一個線程方面沒有可靠的特權或劣勢。
- notifyAll:喚醒在此對象監視器上等待的全部線程。線程經過調用其中一個
wait
方法,在對象的監視器上等待。直到當前線程放棄此對象上的鎖定,才能繼續執行被喚醒的線程。被喚醒的線程將以常規方式與在該對象上主動同步的其餘全部線程進行競爭;例如,喚醒的線程在做爲鎖定此對象的下一個線程方面沒有可靠的特權或劣勢。
package test;
public class Test1 {
private static Object[] objs = new Object[10];
public static void main(String[] args) {
Runnable run1 = new Runnable() {
@Override
public void run() {
synchronized (objs) {
try {
System.out.println(1);
objs.wait();
System.out.println(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
Runnable run2 = new Runnable() {
@Override
public void run() {
synchronized (objs) {
System.out.println(3);
objs.notify();
}
}
};
new Thread(run1).start();
new Thread(run2).start();
}
}
運行結果
1
3
2