JAVA語言規範-線程和鎖章節之同步、等待和通知
JAVA語言規範:線程和鎖
1 同步
JAVA編程語言提供了線程間通信的多種機製。這些方法中最基本的是同步化,此方法是使用監視器實現的。JAVA中每個對象與一個監視器相關聯,一個線程可以加鎖和解鎖監視器。一次僅有一個線程可能在監視器上持有鎖。嚐試鎖住該監視器的任何其他線程被阻塞,直到它們可以再該監視器上獲得一個鎖。線程t可以多次鎖住特別的監視器;每個解鎖將一個加鎖操作的作用反轉來了。
synchronized語句計算了一個對象的引用;然後它嚐試在該對象的監視器上執行加鎖操作,並不進一步繼續,直到鎖操作已經成功完成。在加鎖操作被執行完後,會執行synchronized語句體。如果語句體的執行沒有完成(正常或突然)。那麼將在相同的監視器上自動執行相同的解鎖操作。
JAVA編程語言沒有防止,也沒有要求檢查死鎖條件。線程在多個對象上(直接或間接)持有鎖的程序,應該使用傳統技術來避免死鎖,創建不會死鎖的高級加鎖原語(如果有必要的話)。其他機製(比如讀寫java.util.concurrent 包中的 volatile 變量和類)提供了一些同步的其他方法。
2 等待集合 和 通知
JAVA中的每個對象,都有一個關聯的監視器,也會有一個關聯的等待集合。等待集合是一個線程的集合。
當對象第一次被創建時,它的等待集合為空,增加或移除該集合中的線程,這樣的簡單操作都是原子性的。等待集合受到以下方法的操縱: Object.wait, Object.notify, and Object.notifyAll.。
等待集合的操作也受到線程中斷狀態的的影響,還有Thread類中那些可以進行中斷線程方法的影響。此外在Thread類中的sleep和join的方法,它們能夠獲得等待集合和通知的動作。
2.1等待(Wait)
等待操作由wait()方法的執行引起,或者也可以由限定時間長度的wait()方法觸發。
調用方法wait(long millisecs)或者函數wait(long millisecs, int nanosecs) ,當調用的參數均為0時,這樣的調用等同於wait()。
如果線程在沒有拋出InterruptedException 的情況下返回,那麼線程就從wait 正常返回。
令線程t是在對象m上執行等待方法的線程,並令n是t在m上加鎖操作的編號,這些操作已經不被解鎖操作匹配。下麵的操作之一發生:
- 如果n是0(也就是,線程t已經沒有占用目標m的鎖)則拋出IllegalMonitorStateException 異常。
- 如果這是一個定時等待,並且十億分之一秒參數不在0-999999,或者毫秒參數是負的,那麼就會拋出IllegalArgumentException 的異常。
- 如果線程t被中斷,那就拋出InterruptedException,並將t的中斷狀態設置為假。
否則,下麵的序列發生:
1、線程t被添加到對象m的等待集合中,並在m上執行n個解鎖操作。
2、線程t沒有執行任何的進一步指令,直到它已經從m的等待集合中刪除。由於下麵的操作的任何之一,該程序可能從等待集合中刪除,並在後麵的某個時間繼續。
- 在等待等待集合中刪除而選擇的t的m上正在執行的notify操作。
- 在m上正被執行的notifyAll操作。
- 在t上執行的interrupt操作。
- 如果這是一個定時等待,則為m的等待集合刪除的內部操作,該集合至少在millisecs毫秒加nanosecs十億分之一秒消逝後發生(從寫操作開始)。
- 根據實現的內部操作。實現被允許(盡管不鼓勵)執行“偽造的喚醒”——以便從等待集合中刪除中刪除線程,從而能夠在沒有顯式指令這樣做的情況下再繼續。注意這個裝備成了在循環內使用wait的JAVA編碼實踐的必要條件,這些循環隻有在線程等待持有的某個邏輯條件時才終止。
每個線程必須通過可能導致它從等待集合中刪除的時間確定順序。順序不一定與其他的排序一直,但線程表現得像以那個循環發生的那些事件一樣。
例如,如果線程t在m的等待隊列中,然後t的中斷和m的通知發生,那麼在這些事件上必須有一個順序。如果中斷被認為首先發生,那麼t將最終通過拋出InterruptedException來從wait中中返回,而且中的等待集合的某個其他線程(如果在通知的時候存在的話)必須接收通知。如果通知被認為是首先發生的,那麼t將最終正常地wait返回,且中斷仍然掛起。
- 線程t在m上執行n個加鎖操作。
- 如果由於中斷線程t在步驟2中從m的等待集合中刪除了,那麼t的中斷狀態就被設置為假,並且等待方法拋出InterruptedException。
2.2 通知(notify)
通知操作在調用方法notify和notifyAll 調用之後發生。令線程t是執行對象m上的這些方法的任一方法的線程,並令n是t在m上的加鎖操作的數量,這些操作沒有被解鎖操作匹配。下麵操作之一發生了。
- 如果n是0,則拋出IllegalMonitorStateException。情形是這樣的:線程t已經沒有占有目標m的鎖。
- 如果n大於0,並且這是一個notify操作,那麼如果m的等待集合不是空的,則是m的當前等待集合的一個成員的線程u被選擇,並從等待集合中刪除(不保證在等待集合中選定哪個線程)。從等待集合中進行該刪除讓u在等待操作中得以繼續。但注意,繼續之後的u的加鎖操作不能成功,直到t完全解鎖m的監視器後的某個時間。
- 如果n大於0,並且這是一個notifyAll操作,那麼所有的線程就從m的等待集合中刪除並繼續,但請注意。它們當中僅有的一個將一次鎖住wait的繼續期間需要的監視器
2.3 中斷
中斷操作在調用方法Thread.interrupt及定義來依次調用它的方法(比如ThreadGroup.interrupt)之後發生。對於某個線程u,令t是調用u.interrupt的線程,其中t和u可能是相同的。此操作導致u的中斷狀態被設置為真。
另外,如果存在著等待集合包含u的某個對象m,那麼u就從m的等待集合中刪除。這使得u能夠在等待操作中繼續,在該操作的情況中,此等待將在重新加鎖m的監視器後拋出InterruptedException。
調用Thread.isInterrupted可以確定線程的中斷狀態。靜態方法Thread.interrupted有線程調用來觀察和清除自己的中斷狀態。
2.4 等待、通知和中斷的交互
上麵的規範允許我們確定於等待、通知和中斷有關的幾個屬性。如果在等待時,線程同時是通知和中斷的,它就可能是下麵之一:
- 從wait正常返回,盡管仍然有掛起中斷(在其他工作中,對Thread.interrupted的調用將返回真)。
- 通過拋出InterruptedException從wait處返回。
線程不可以重置它的中斷狀態,並從wait的調用中正常返回。
同樣,通知不能由於中斷而丟失。假定線程的集合s在對象m的等待集合中,並且另一個線程在m上執行notify,那麼有下麵之一發生:
- 至少s中有一個線程從wait處正常返回,或者
- s中的所有線程必須通過拋出InterruptedException退出wait。
注意,如果線程通過notify被中斷和喚醒,並且線程通過拋出InterruptedException從wait返回,那麼等待集合中的某個線程必須被通知到。
最後更新:2017-05-22 20:04:34