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