一旗唁、前言
Java中的鎖有各種特性,比如可重入與不可重入宴倍,公平與非公平等张症,也可以根據(jù)這些特性進(jìn)行分類。
二鸵贬、可重入鎖
- 當(dāng)某個(gè)線程請(qǐng)求一個(gè)由其他線程持有的鎖的時(shí)俗他,發(fā)出請(qǐng)求的的線程就會(huì)阻塞。但當(dāng)一個(gè)線程再次請(qǐng)求自己持有的對(duì)象鎖時(shí)阔逼,如果能請(qǐng)求成功兆衅,說明這個(gè)鎖是可重入的。也就是說線程可以重復(fù)的獲得已經(jīng)持有的鎖嗜浮;
- 拿synchronized來說羡亩,synchronized的鎖是原子性的,也是可重入的危融,因此一個(gè)線程調(diào)用synchronized方法的同時(shí)在其方法體內(nèi)部調(diào)用該對(duì)象的另一個(gè)synchronized方法將不會(huì)進(jìn)入阻塞狀態(tài)畏铆,也就是說一個(gè)線程得到對(duì)象鎖后再次請(qǐng)求該對(duì)象鎖是允許的,這就是synchronized的可重入性专挪。
看一段代碼:
public class ThreadTest extends SuperClass {
public synchronized void test(Thread thread) {
System.out.println(thread.getName());
super.test();
}
}
class SuperClass {
public synchronized void test() {
System.out.println(Thread.currentThread().getName());
}
}
??子類重寫了父類的test方法及志,然后調(diào)用父類的方法片排,此時(shí)如果沒有可重入的鎖寨腔,那么這段代碼將會(huì)產(chǎn)生死鎖。因?yàn)閮蓚€(gè)方法都是synchronized方法率寡,因此每個(gè)test方法再執(zhí)行前都會(huì)獲取SuperClass對(duì)象鎖迫卢,然后如果該鎖不是可重入的,那么在調(diào)用super.test方法時(shí)將無法獲得SuperClass對(duì)象上的鎖冶共,因?yàn)檫@個(gè)鎖已經(jīng)被持有乾蛤,從而線程將永遠(yuǎn)停頓下去每界,等待一個(gè)永遠(yuǎn)也無法獲得的鎖。
- 可重入鎖的一種實(shí)現(xiàn)方式是為每個(gè)鎖關(guān)聯(lián)一個(gè)計(jì)數(shù)值和一個(gè)所有者線程家卖。當(dāng)計(jì)數(shù)器為0時(shí)眨层,這個(gè)鎖就被認(rèn)為是沒有被任何線程持有,當(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í)捺疼,該鎖被釋放。
- 可重入鎖永罚,有時(shí)也被稱為遞歸鎖啤呼,也就是說外層代碼獲取該鎖之后,內(nèi)層遞歸函數(shù)仍然可以獲得該鎖呢袱,JAVA中媳友,
ReentrantLock
和synchronized
都是 可重入鎖;- 可重入鎖的最大作用就是可以避免死鎖产捞;
二醇锚、可中斷鎖
- 可中斷鎖,顧名思義就是可以中斷阻塞中的鎖坯临,Java中的synchronized由于不可中斷焊唬,不是可中斷鎖,而Lock則是可中斷鎖看靠。
- 通俗的來說赶促,如果某一線程A正在執(zhí)行鎖中的代碼,另一線程B正在等待獲取該鎖挟炬,可能由于等待時(shí)間過長鸥滨,線程B不想等待了,想先處理其他事情谤祖,我們可以讓它中斷自己或者在別的線程中中斷它婿滓,這種就是可中斷鎖。
三粥喜、讀寫鎖
??就是說對(duì)資源的訪問操作分為讀鎖和寫鎖凸主,分開來進(jìn)行。就是因?yàn)橛辛俗x寫鎖额湘,才使得多個(gè)線程之間的讀操作不會(huì)發(fā)生沖突卿吐,對(duì)應(yīng)的接口是ReadWriteLock旁舰,該接口對(duì)應(yīng)的實(shí)現(xiàn)類是ReentrantReadWriteLock,可以通過對(duì)應(yīng)的readLock()方法獲取讀鎖嗡官,通過writeLock()方法獲取寫鎖箭窜,后續(xù)我們會(huì)學(xué)習(xí)到。
四衍腥、公平鎖
??公平鎖是說根據(jù)請(qǐng)求鎖的順序來獲取鎖绽快,比如同時(shí)有多個(gè)線程在等待獲取鎖的時(shí)候,最先請(qǐng)求的線程會(huì)獲取該鎖(也就是等待時(shí)間最長的線程)紧阔,這種就是公平鎖坊罢。非公平鎖就無法保證這點(diǎn)了,但需要注意下:
- Java中的synchronized是一種非公平鎖擅耽,無法保證最先請(qǐng)求的線程先獲取到鎖活孩,而ReentrantLock和ReentrantReadWriteLock,默認(rèn)情況下是非公平鎖乖仇,但可以設(shè)置為公平鎖憾儒,后續(xù)我們學(xué)習(xí)到這里的時(shí)候再說。
- 對(duì)ReentrantLock來說乃沙,即使是公平鎖起趾,對(duì)于可輪詢的tryLock仍然有可能會(huì)出現(xiàn)“插隊(duì)”的現(xiàn)象,也就是不按線程請(qǐng)求順序的情況警儒。所以說即使是公平鎖训裆,也不一定能確保線程調(diào)度器是公平的。如果線程調(diào)度器選擇忽略一個(gè)線程蜀铲,而該線程恰好是等待時(shí)間最長的那個(gè)边琉,那么就沒有機(jī)會(huì)公平的處理這個(gè)鎖了。
- 公平鎖的實(shí)現(xiàn)需要通過一個(gè)隊(duì)列來維護(hù)線程請(qǐng)求的順序性记劝,并且在進(jìn)行等待和喚醒線程時(shí)存在一定的性能開銷变姨,而非公平性則不用考慮這些,所以大多數(shù)情況下厌丑,非公平鎖的性能要高于公平鎖的性能定欧。但當(dāng)持有鎖的實(shí)際相對(duì)較長,或者請(qǐng)求鎖的平均時(shí)間間隔較長的時(shí)候怒竿,這時(shí)候可以使用公平鎖砍鸠。
本文參考自:
海子-Java并發(fā)編程-Lock
《Java并發(fā)編程實(shí)戰(zhàn)》
《Java核心技術(shù)》