一篇文章徹底搞懂面試中常被問(wèn)的各種“鎖”
前言
鎖,顧名思義就是鎖住一些資源,當(dāng)只有我們拿到鑰匙的時(shí)候,才能操作鎖住的資源。在我們的Java,數(shù)據(jù)庫(kù),還有一些分布式的環(huán)境中,總是充斥著各種各樣的鎖讓人頭疼,例如“公平鎖”、“自旋鎖”、“讀寫(xiě)鎖”、“分布式鎖”等等。
其實(shí)真實(shí)的情況是,鎖并沒(méi)有那么多,很多概念只是從不同的功能特性,設(shè)計(jì),以及鎖的狀態(tài)這些不同的側(cè)重點(diǎn)來(lái)說(shuō)明的,因此我們可以根據(jù)不同的分類(lèi)來(lái)搞明白為什么會(huì)有這些“鎖”?坐穩(wěn)扶好了,準(zhǔn)備開(kāi)車(chē)。
正文
“公平鎖”與“非公平鎖”
- 公平鎖:指線程在等待獲取同一個(gè)鎖的時(shí)候,是嚴(yán)格按照申請(qǐng)鎖的時(shí)間順序來(lái)進(jìn)行的,這就意味著在程序正常運(yùn)作的時(shí)候,不會(huì)有線程執(zhí)行不到,而被“餓死”,但是也需要額外的機(jī)制來(lái)維護(hù)這種順序,所以效率相對(duì)于非公平鎖會(huì)差點(diǎn)。
- 非公平鎖:概念跟“公平鎖”恰恰相反,隨機(jī)線程獲取鎖,相率相對(duì)高。
new ReentrantLock(); //默認(rèn)非公平鎖 new ReentrantLock(true); //公平鎖
“重入鎖(遞歸鎖)”與“不可重入鎖(自旋鎖)”
這里要注意了,重入/遞歸,不可重入/自旋,雖然名字不同,但是確實(shí)是同一種鎖,只是從鎖的表現(xiàn)跟實(shí)現(xiàn)方式的角度來(lái)命名而已。
重入鎖:當(dāng)一個(gè)線程獲取了A鎖以后,若后續(xù)方法運(yùn)行被A鎖鎖住的話,當(dāng)前線程也是可以直接進(jìn)入的。
public class Demo { private Lock lockA; public Demo(Lock Lock) { this.lockA = lock; } public void methodA() { lockA.lock(); methodB(); lockA.unlock(); } public void methodB() { lockA.lock(); //dosm lockA.unlock(); } }
當(dāng)我們運(yùn)行methodA()的時(shí)候,線程獲取了lockA,然后調(diào)用methodB()的時(shí)候發(fā)現(xiàn)也需要lockA,由于這是一個(gè)可重入鎖,所以當(dāng)前線程也是可以直接進(jìn)入的。在java中,synchronized跟ReetrantLock都是可重入鎖。
不可重入鎖:以上面的代碼實(shí)例來(lái)說(shuō)明,就是methodA進(jìn)入methodB的時(shí)候不能直接獲取鎖,必須先調(diào)用unLock釋放鎖。才能執(zhí)行下去,那實(shí)現(xiàn)不可重入鎖有什么方式呢?那就是自旋,所以會(huì)有一個(gè)小名叫做自旋鎖。
public class SpinLock { private AtomicReference<Thread> sign =new AtomicReference<>(); public void lock(){ Thread current = Thread.currentThread(); while(!sign .compareAndSet(null, current)){ } } public void unlock (){ Thread current = Thread.currentThread(); sign .compareAndSet(current, null); } }
“悲觀鎖”與“樂(lè)觀鎖”
這兩種鎖呢,其實(shí)是一個(gè)很宏觀的分類(lèi),它不是一種具體的鎖,而是泛指看待并發(fā)的程度。
悲觀鎖:有一個(gè)“悲觀”的心態(tài),既每次取數(shù)據(jù)的時(shí)候,都會(huì)認(rèn)為該數(shù)據(jù)會(huì)被修改,所以必須加一把鎖才安心。
樂(lè)觀鎖:樂(lè)觀的孩子,認(rèn)為同一個(gè)數(shù)據(jù)不會(huì)發(fā)生并發(fā)操作的行為,所以取的時(shí)候不會(huì)加鎖,只有在更新的時(shí)候,會(huì)通過(guò)例如版本號(hào)之類(lèi)的來(lái)判斷是否數(shù)據(jù)被修改了。
Java中各種鎖其實(shí)都是悲觀鎖的實(shí)現(xiàn),既操作的數(shù)據(jù)的都會(huì)被獲取鎖的線程鎖住,而樂(lè)觀鎖的話,一般是通過(guò)cas(compare and swap)的思想來(lái)實(shí)現(xiàn),例如一些原子類(lèi)AtomicInteger使用自旋來(lái)原子更新。
“共享鎖”與“排他鎖”
這兩種鎖的概念比較多的出現(xiàn)在數(shù)據(jù)庫(kù)的事務(wù)當(dāng)中。
共享鎖:也稱(chēng)讀鎖或S鎖。如果事務(wù)對(duì)數(shù)據(jù)A加上共享鎖后,則其他事務(wù)只能對(duì)A再加共享鎖,不能加排它鎖。獲準(zhǔn)共享鎖的事務(wù)只能讀數(shù)據(jù),不能修改數(shù)據(jù)。在java中的ReetrantReadWriteLock()也是如此。
排它鎖:也稱(chēng)獨(dú)占鎖、寫(xiě)鎖或X鎖。如果事務(wù)對(duì)數(shù)據(jù)A加上排它鎖后,則其他事務(wù)不能再對(duì)A加任何類(lèi)型的鎖。獲得排它鎖的事務(wù)即能讀數(shù)據(jù)又能修改數(shù)據(jù)。
分布式鎖
我們上面聊的這些鎖,都是在單個(gè)程序上面的不同線程之間來(lái)實(shí)現(xiàn)的,那么當(dāng)我們的不同程序需要去競(jìng)爭(zhēng)同一塊資源的時(shí)候,這就需要分布式鎖了,我們可以通過(guò)redis、zookeeper等中間件來(lái)實(shí)現(xiàn)分布式鎖。
對(duì)于鎖來(lái)說(shuō),其實(shí)還有偏向鎖,輕量級(jí)鎖等,但是這里涉及到的內(nèi)容就比較多,這里就不在展開(kāi)篇幅介紹了,有興趣的同學(xué)可自行研究,如果你能搞懂上面介紹的這些鎖,那基本上在絕大部分的公司關(guān)于“鎖”的問(wèn)題都可以迎刃而解。
總結(jié)
以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,謝謝大家對(duì)腳本之家的支持。
相關(guān)文章
Java控制臺(tái)輸入數(shù)組并逆序輸出的方法實(shí)例
這篇文章主要介紹了Java手動(dòng)輸入數(shù)組并逆向輸出的方法實(shí)例,需要的朋友可以參考下。2017-08-08SpringCloud網(wǎng)關(guān)(Zuul)如何給多個(gè)微服務(wù)之間傳遞共享參數(shù)
這篇文章主要介紹了SpringCloud網(wǎng)關(guān)(Zuul)如何給多個(gè)微服務(wù)之間傳遞共享參數(shù),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-03-03Spring Boot+Mybatis+Pagehelper分頁(yè)實(shí)現(xiàn)
本篇文章主要講述的是Spring Boot+Mybatis+Pagehelper分頁(yè)實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-04-04java關(guān)于String.split("|")的使用方式
這篇文章主要介紹了java關(guān)于String.split("|")的使用方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-02-02Mybatis配置錯(cuò)誤:java.lang.ExceptionInInitializerError
這篇文章主要介紹了Mybatis配置錯(cuò)誤:java.lang.ExceptionInInitializerError的相關(guān)資料,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-12-12Java?ASM使用logback日志級(jí)別動(dòng)態(tài)切換方案展示
這篇文章主要介紹了Java?ASM使用logback日志級(jí)別動(dòng)態(tài)切換方案展示,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步早日升職加薪2022-04-04