終于把Java鎖分類理清楚了
1.內(nèi)置鎖與顯式鎖
開發(fā)人員根據(jù)書寫方式將鎖分為內(nèi)置鎖和顯式鎖,這個(gè)分類純粹是因?yàn)镴ava中的鎖分為兩大派:老派鎖和新派鎖。synchronized一人開一派,曾獨(dú)占鰲頭數(shù)十載,是老派鎖的代表,新派則是以AQS為首新興門派,其子子孫孫遍布并發(fā)編程的各個(gè)角落。
synchronized在書寫上只需要在方法名或者靜態(tài)代碼塊中聲明synchronized關(guān)鍵字即可,其加鎖,釋放鎖均是由程序自行處理,因而得名內(nèi)置鎖。
而新派鎖AQS門下的鎖基本都是需要手動(dòng)聲明加鎖和釋放鎖的,如果程序員忘記寫釋放鎖的代碼就會(huì)造成鎖永遠(yuǎn)不會(huì)釋放,其門下應(yīng)用最多且與synchronized能對(duì)等換的當(dāng)屬如ReentrantLock,因?yàn)橛辛孙@式鎖的說法。
內(nèi)置鎖不需要程序員操心上鎖和釋放鎖的邏輯,但是在應(yīng)用靈活度上有些欠缺,比如說控制鎖的范圍,內(nèi)置鎖就不能很好的縮小鎖范圍。
顯示鎖因?yàn)槠湫枰謩?dòng)加鎖和釋放鎖,所以具備更高的控制加鎖范圍的靈活性。因?yàn)槠滏i的實(shí)現(xiàn)是在Java層面實(shí)現(xiàn),作為Java開發(fā)者可以靈活優(yōu)化其功能,所以其功能可以更多,而synchronized是由jvm層面實(shí)現(xiàn),作為Java開發(fā)者就沒有辦法修改到j(luò)vm底層所以功能上會(huì)有局限性。
2. 可重入鎖與不可重入鎖
我們知道當(dāng)前線程獲取到鎖后,另外的線程就獲取不到這把鎖,那么當(dāng)前線程如果再次獲取這把鎖的時(shí)候,是否能獲取到呢,如果能獲取到就是可重入鎖,如果獲取不到就是不可重入鎖,即是否可重入說的是線程自己是否能夠多次獲取鎖。
這里需要注意的是可重入鎖,重入幾次,釋放鎖的時(shí)候也要釋放幾次,synchronized和ReentrantLock是可重入鎖,且新派鎖中作為aqs的子子孫孫很容易實(shí)現(xiàn)不可重入的鎖,程序員也可以手動(dòng)根據(jù)可重入鎖實(shí)現(xiàn)不可重入鎖。但是在Java中基本沒有提供現(xiàn)成的不可重入鎖實(shí)現(xiàn)類。
3. 公平鎖與非公平鎖
顧名思義,公平不公平就是大家是否有秩序的去競(jìng)爭(zhēng)鎖資源,何為有秩序,那就是排隊(duì)唄,講究先來后到。
老派鎖的代表synchronized是一個(gè)實(shí)打?qū)嵉姆枪芥i,它的非公平是說沒有搶到鎖的線程都會(huì)被阻塞并放到某個(gè)集合中,這個(gè)集合是jvm底層實(shí)現(xiàn),我們暫不關(guān)心,當(dāng)占有鎖的線程釋放鎖后,jvm會(huì)在這個(gè)集合中隨機(jī)喚醒一個(gè)去獲取鎖,這里的非公平重在隨機(jī)。
我們說了新派鎖是基于AQS,所以其子孫后代鎖在功能上會(huì)有更高的靈活性。以ReentrantLock為例,它默認(rèn)情況下是非公平鎖,但是可以通過傳參實(shí)現(xiàn)公平鎖。其非公平和老派鎖有點(diǎn)不一樣,例如,線程1獲取了鎖后,線程2 線程3因?yàn)楂@取不到鎖而進(jìn)入阻塞隊(duì)列進(jìn)行排隊(duì)阻塞,當(dāng)有線程4試圖獲取鎖資源的時(shí)候,如果聲明的是公平鎖,線程4會(huì)直接進(jìn)入隊(duì)列排隊(duì),如果聲明的是非公平鎖,線程4會(huì)直接搶鎖,即這里的非公平說的是新進(jìn)來的線程與阻塞隊(duì)列頭部的線程爭(zhēng)奪鎖資源,強(qiáng)調(diào)的是新線程與隊(duì)列老大的競(jìng)爭(zhēng)。
新老派鎖的非公平都會(huì)不同成都的造成,某些或則某個(gè)線程一直拿不到鎖,但是似乎老派鎖synchronized造成這種現(xiàn)象的概率大一點(diǎn)。
4.共享鎖與獨(dú)占鎖
既然是鎖,那一定是某些線程不能進(jìn)入,但是哪些線程能進(jìn)入,就造就了共享鎖和獨(dú)占鎖的區(qū)分,顧名思義,只允許一個(gè)線程獨(dú)享的鎖就是獨(dú)占鎖,允許多個(gè)線程共同進(jìn)入的鎖就是共享鎖。
老派鎖synchronized和新派鎖ReentrantLock都是獨(dú)占鎖,而新派鎖AQS門下,有一些鎖是共享鎖,例如:ReentrantReadWriteLock,Semaphore,CountDownLatch,CyclicBarrier等
5. 樂觀鎖與悲觀鎖
鎖的作用就是將某段業(yè)務(wù)邏輯保護(hù)起來,更深一點(diǎn)就是將邏輯中共享數(shù)據(jù)保護(hù)起來,防止多個(gè)線程操作共享數(shù)據(jù)的時(shí)候出現(xiàn)問題。然而開發(fā)者根據(jù)鎖的情感態(tài)度將鎖分為樂觀鎖和悲觀鎖,我們上面說的老派鎖和新派鎖,他們都對(duì)共享數(shù)據(jù)的保護(hù)持悲觀態(tài)度,他們認(rèn)為所有的線程都有可能侵害共享數(shù)據(jù),因此早早在業(yè)務(wù)邏輯前加上鎖,每來一個(gè)線程都要進(jìn)行排查。并且只允許一個(gè)線程進(jìn)入。所以他們都是悲觀鎖。
然而就在新派鎖崛起的同一時(shí)期,遙遠(yuǎn)的北方有一支異軍突起,他們劍走偏鋒,他們認(rèn)為所有人在沒有露出魔鬼爪牙前都是好人,他們持一種樂觀的態(tài)度對(duì)待共享數(shù)據(jù)的保護(hù),但是他們并不是不保護(hù),只是保護(hù)的方式是,當(dāng)某個(gè)線程對(duì)共享數(shù)據(jù)操作前,必須先獲取一個(gè)令牌,然后拿著令牌去操作共享數(shù)據(jù),當(dāng)共享數(shù)據(jù)保存的令牌與整個(gè)線程持有的令牌匹配正確后才能操作。如果匹配不上就操作失敗。
在操作數(shù)據(jù)失敗后應(yīng)該怎么操作,就需要開發(fā)者自行處理,或者直接返回失敗信息,或者再次重試,一般情況下,樂觀鎖會(huì)結(jié)合自旋重試來使用,但是當(dāng)并發(fā)很高的時(shí)候,會(huì)造成很多線程自旋,給cpu帶來壓力,所以適用的場(chǎng)景有限。
總結(jié)
鎖,是排他的一種機(jī)制,排他性是鎖的統(tǒng)一標(biāo)準(zhǔn),而根據(jù)其排他的積極程度,分為樂觀鎖和悲觀鎖,而悲觀鎖又分為兩大派系,一個(gè)是以synchronized為首的老派鎖,一個(gè)是以AQS為首的新派鎖。
而公平與非公平,可重入與不可重入,共享與獨(dú)占,內(nèi)置與顯式,可中斷與不可中斷都是鎖的特性。
接下來就分開來介紹其底層實(shí)現(xiàn)。