# Java線程(篇外篇):線程和鎖
## 前言
本文翻譯自JLS7(Java Language Specification)第17章,與大家分享。文中的英文還不知道怎么譯,持續更新。
英文原文:[http://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html](http://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html)。
## 概述
前面章節大部分討論的是只關心代碼在同一時間執行一條語句或表達式的行為,也就是單線程執行,Java虛擬機同時可以支持多個線程執行。多個線程能夠獨立的執行代碼,代碼通常會操作共享主內存中的值和對象。多線程可以被多硬件處理器、時間分片單硬件處理器、時間分片多硬件處理器支持。
Java中線程由Thread類來代表。用戶創建線程唯一的方式就是創建該類的對象;每一個線程都與這樣的對象關聯。當相應Thread對象的start()方法被調用時,一個線程就啟動了。
線程的執行會產生不可預知的行為,尤其是在沒有正確同步的情況下。本章描述了多線程程序的語義;它包含了被多線程更新的共享內存的值是否可見的規則。為了規范不同硬件架構中相似的內存模型,這些語義被稱為Java程序語言內存模型。當沒有爭論出現時,我們將這些規則簡稱為"內存模型"。
這些語義沒有規定多線程程序如何被執行。相反,它們描述了多線程程序能夠展現出的行為。產生唯一允許行為的任何一個執行策略都是一個可接受的執行策略。
## 同步(Synchronization)
Java程序語言提供了多種機制用于線程通信。這些方法中最基本的就是同步,同步使用監視器實現。Java中每個對象都與一個監視器關聯,線程基于某個對象來實現持有鎖或者釋放鎖。同一時間只有一個線程可以持有監視器上的鎖。此時,其它線程在嘗試持有該監視器上的鎖時,都將被阻塞,直到原來的線程釋放該監視器上的鎖。一個線程t也許會鎖定特定的監視器多次; 每次解鎖反轉一個鎖定操作的效果。
synchronized語句([§14.19](http://docs.oracle.com/javase/specs/jls/se7/html/jls-14.html#jls-14.19))使用一個對象的引用作為監視器,在執行其代碼塊之前,會先嘗試對該監視器對象進行鎖定操作。鎖定操作成功完成后,會進一步執行代碼塊。如果代碼塊執行完,包括正常的或者異常的,那么該監視器上的鎖會自動的釋放。
synchronized方法([§8.4.3.6](http://docs.oracle.com/javase/specs/jls/se7/html/jls-8.html#jls-8.4.3.6 "8.4.3.6.?synchronized Methods"))被調用時,會自動進行鎖定操作;直到鎖定操作成功的完成,方法體才被執行。如果該方法是一個非靜態方法,監視器對象是該方法所在類的一個實例。如果該方法是一個靜態方法,監視器對象是該方法所在類的Class對象。如果方法體執行完,包括正常的或者異常的,那么該監視器上的鎖會自動的釋放。
Java程序語言既不阻止也不需要檢測死鎖條件。當多線程程序直接或間接的持有多個對象上的鎖時應該使用常規的技術來避免死鎖問題,如果必要的話,創建更高級別的不會死鎖的鎖定原語。
還有其它的機制,如volatile變量的讀寫和使用java.util.concurrent包中的類,提供了同步的替代方法。
## 等待集合和通知(Wait Sets and Notification)
每一個對象,除了有關聯的監視器,還有一個關聯的等待集合。等待集合中存儲的是線程。
當一個對象被創建時,它的等待集合是空的。等待集合中線程的增加和刪除這兩個基本操作是原子的。對等待集合的操作只有通過Object.wait、Object.notify、Object.notifyAll三個方法。
等待集合操作也可以被線程的中斷狀態、線程類處理中斷的方法所影響。另外,線程類的睡眠和合并方法擁有那些等待和通知操作的衍生屬性。
## 等待(Wait)
wait()或帶時間參數的wait(long millisecs)和wait(long millisecs, int nanosecs)被調用時會觸發等待操作。
*調用wait(0)或wait(0, 0)是和wait()等價的。*
一個線程從等待中正常返回如果它沒有拋出InterruptedException。
設線程t為執行m對象wait方法的線程,and let n be the number of lock actions by t on m that have not been matched by unlock actions。則會發生下面這些情況之一:
* 如果n=0(也就是線程t已經不再持有對象m的鎖),將會拋出IllegalMonitorStateException。
* 如果是帶時間參數的wait方法,并且nanosecs參數不在0-999999范圍內或者millisecs是負數,將會拋出IllegalArgumentException。
* 如果線程t被中斷,將會拋出InterruptedException并且線程t的中斷狀態設為false。
* 否則,以下情況會順序發生:
1. 線程t被添加到m對象的等待集合中,并釋放m上的鎖。
2. 線程t直到從對象m的等待集合中移除后,才會執行后面的代碼。直到下列的情況發生任意一種時,線程才會從等待隊列中移除,并重新進行線程調度:
* 其他某個線程調用對象m的 notify 方法,并且線程t碰巧被任選為被喚醒的線程。
* 其他某個線程調用對象m的?notifyAll?方法。
* 其他某個線程中斷了線程t。
* 如果是帶時間參數的wait方法,從wait操作時間開始,millisecs毫秒數+nanosecs納秒數指定的超時時間已用完。
* 虛假喚醒(spurious wake-ups)。*為了防止虛假喚醒,wait操作必須放在循環中。*
每個線程對于能引起它從等待集合中移除的事件都必須有一個確定的執行順序。該順序不必與其它的排序相一致,但是該線程必須表現得好像這些事件是按照這個順序發生的。
例如,假設一個線程t在對象m的等待集合中,線程t的中斷和對象m的通知兩個事件同時發生,那么這兩個事件必須有一個執行順序。如果中斷事件先執行,線程t會拋出一個InterruptedException并從等待中退出,并且對象m的等待集合中的其它線程會接收到通知。如果通知事件先執行,線程t從等待中正常退出,中斷事件被掛起。
6. 線程t在m上執行鎖操作。
7. 如果線程t是在第2部的時候從m的等待集合中移除,t的中斷狀態設置為false并且wait方法拋出InterruptedException。
## 通知(Notification)
notify和notifyAll被調用時會觸發通知操作。
設線程t為執行m對象通知方法的線程,and let n be the number of lock actions by t on m that have not been matched by unlock actions。則會發生下面這些情況之一:
* 如果n=0,將會拋出IllegalMonitorStateException。這是線程t已經不再持有對象m的鎖的情況。
* 如果n>0并且這是一個notify操作,如果m的等待集合非空,m的等待集合中一個線程u被選中,從等待集合中移除。等待集合中哪一個線程被選中是沒有規則的。從等待集合中移除使u從等待狀態恢復。然而,在u恢復后,直到線程t釋放了在監視器的所之后,u才能持有鎖。
* 如果n>0并且這是一個notifyAll操作,所有的線程都會從m的等待集合中移除,重新開始競爭。然而,恢復后,它們之中只有一個會持有監視器的鎖。
## 中斷(Interruptions)
Thread.interrupt、ThreadGroup.interrupt被調用時會觸發中斷操作。
設t為執行u.interrupt的線程,該操作會將線程u的中斷狀態設為true。
另外,如果m的等待集合中包含u,u會被移出。This enables u to resume in a wait action, in which case this wait will, after re-locking m's monitor, throw InterruptedException。
調用Thread.isInterrupted能確定一個線程的中斷狀態。靜態方法Thread.interrupted也許會被一個守護線程調用,并清除它自己的中斷狀態。
## 等待、通知、中斷的相互影響(Interactions of Waits, Notification, and Interruption)
待譯。
## 睡眠和讓步(Sleep and Yield)
??待譯。
- 前言
- Java線程(一):線程安全與不安全
- Java線程(二):線程同步synchronized和volatile
- Java線程(三):線程協作-生產者/消費者問題
- Java線程(四):線程中斷、線程讓步、線程睡眠、線程合并
- Java線程(五):Timer和TimerTask
- Java線程(六):線程池
- Java線程(七):Callable和Future
- Java線程(八):鎖對象Lock-同步問題更完美的處理方式
- Java線程(九):Condition-線程通信更高效的方式
- Java線程(十):CAS
- Java線程(十一):Fork/Join-Java并行計算框架
- Java線程(篇外篇):阻塞隊列BlockingQueue
- Java線程(篇外篇):線程本地變量ThreadLocal
- Java線程(篇外篇):線程和鎖