每個(gè) Java 對(duì)象都可以用做一個(gè)實(shí)現(xiàn)同步的鎖,這些鎖被稱為內(nèi)置鎖或監(jiān)視器鎖。線程在進(jìn)入同步代碼塊之前會(huì)自動(dòng)獲取鎖,并且在退出同步代碼塊時(shí)會(huì)自動(dòng)釋放鎖。獲得內(nèi)置鎖的唯一途徑就是進(jìn)入由這個(gè)鎖保護(hù)的同步代碼塊或方法。
當(dāng)某個(gè)線程請(qǐng)求一個(gè)由其他線程持有的鎖時(shí),發(fā)出請(qǐng)求的線程就會(huì)阻塞。然而,由于內(nèi)置鎖是可重入的,因此如果摸個(gè)線程試圖獲得一個(gè)已經(jīng)由它自己持有的鎖,那么這個(gè)請(qǐng)求就會(huì)成功。“重入”意味著獲取鎖的操作的粒度是“線程”,而不是調(diào)用。重入的一種實(shí)現(xiàn)方法是,為每個(gè)鎖關(guān)聯(lián)一個(gè)獲取計(jì)數(shù)值和一個(gè)所有者線程。當(dāng)計(jì)數(shù)值為 0 時(shí),這個(gè)鎖就被認(rèn)為是沒(méi)有被任何線程所持有,當(dāng)線程請(qǐng)求一個(gè)未被持有的鎖時(shí),JVM 將記下鎖的持有者,并且將獲取計(jì)數(shù)值置為 1,如果同一個(gè)線程再次獲取這個(gè)鎖,計(jì)數(shù)值將遞增,而當(dāng)線程退出同步代碼塊時(shí),計(jì)數(shù)器會(huì)相應(yīng)地遞減。當(dāng)計(jì)數(shù)值為 0 時(shí),這個(gè)鎖將被釋放。
重入進(jìn)一步提升了加鎖行為的封裝性,因此簡(jiǎn)化了面向?qū)ο蟛l(fā)代碼的開(kāi)發(fā)。分析如下程序:
public class Father
{
public synchronized void doSomething(){
......
}
}
public class Child extends Father
{
public synchronized void doSomething(){
......
super.doSomething();
}
}
子類覆寫(xiě)了父類的同步方法,然后調(diào)用父類中的方法,此時(shí)如果沒(méi)有可重入的鎖,那么這段代碼件產(chǎn)生死鎖。
由于 Fither 和 Child 中的 doSomething 方法都是 synchronized 方法,因此每個(gè) doSomething 方法在執(zhí)行前都會(huì)獲取 Child 對(duì)象實(shí)例上的鎖。如果內(nèi)置鎖不是可重入的,那么在調(diào)用 super.doSomething 時(shí)將無(wú)法獲得該 Child 對(duì)象上的互斥鎖,因?yàn)檫@個(gè)鎖已經(jīng)被持有,從而線程會(huì)永遠(yuǎn)阻塞下去,一直在等待一個(gè)永遠(yuǎn)也無(wú)法獲取的鎖。重入則避免了這種死鎖情況的發(fā)生。
同一個(gè)線程在調(diào)用本類中其他 synchronized 方法/塊或父類中的 synchronized 方法/塊時(shí),都不會(huì)阻礙該線程地執(zhí)行,因?yàn)榛コ怄i時(shí)可重入的。