一個(gè)線程中的多個(gè)流程能不能獲取同一把鎖:可重入鎖和非可重入鎖
- 可重入鎖
可重入性:表明了鎖的分配機(jī)制,是基于線程的分配,不是基于方法調(diào)用的分配。
是指在一個(gè)線程中可以多次獲取同一把鎖,避免了死鎖。
實(shí)現(xiàn)原理:為每個(gè)鎖關(guān)聯(lián)一個(gè)請(qǐng)求計(jì)數(shù)器和一個(gè)占有它的線程。計(jì)數(shù)器為0時(shí),鎖是未被占有的;線程占有鎖時(shí),計(jì)數(shù)器為1;之后線程繼續(xù)再次請(qǐng)求時(shí),計(jì)數(shù)器遞增。當(dāng)占用線程退出同步塊,計(jì)數(shù)器遞減,當(dāng)計(jì)數(shù)器為0時(shí),鎖被釋放。
例子:在一個(gè)線程中執(zhí)行一個(gè)帶鎖的方法,在該方法中又調(diào)用了另一個(gè)需要相同鎖的方法,線程可以直接執(zhí)行調(diào)用的方法,不需要重新獲得鎖。
- ReentrantLock和synchronized都是可重入鎖。
不可重入鎖:會(huì)出現(xiàn)死鎖。
不能多次獲取同一把鎖。
多個(gè)線程競(jìng)爭(zhēng)鎖時(shí)要不要排隊(duì):公平鎖和非公平鎖
- 公平鎖:
排隊(duì)
以請(qǐng)求鎖的順序來(lái)獲取鎖,有多個(gè)線程在等待一個(gè)鎖時(shí),等待時(shí)間最久的線程會(huì)獲得鎖。
通過(guò)同步隊(duì)列來(lái)實(shí)現(xiàn)多個(gè)線程按照申請(qǐng)鎖的順序來(lái)獲取鎖。
非公平鎖:先嘗試插隊(duì),插隊(duì)失敗再排隊(duì)。即多線程在獲取鎖時(shí)直接嘗試獲取鎖,獲取不到時(shí)才會(huì)到等待隊(duì)列的隊(duì)尾等待。
無(wú)法保證鎖的獲取是按照請(qǐng)求鎖的順序進(jìn)行的。
synchronized是非公平鎖。
ReentrantLock和ReentrantReadWriteLock默認(rèn)情況下是非公平鎖,可以設(shè)置為公平鎖。在構(gòu)造兩者的對(duì)象時(shí),在構(gòu)造函數(shù)中傳入的是true則為公平鎖,false時(shí)則為非公平鎖。
線程獲取鎖失敗,線程要不要阻塞?
獲取鎖失敗時(shí),線程不進(jìn)行阻塞時(shí),分為自旋鎖和適應(yīng)性自旋鎖。
- 自旋鎖
用于線程之間的同步,是一種非阻塞鎖。
在線程沒(méi)有獲取到鎖時(shí)不進(jìn)入阻塞狀態(tài),不放棄CPU時(shí)間片,在原地忙等,一直循環(huán)著,線程反復(fù)檢查鎖變量是否可以用,等待獲取到鎖的線程釋放鎖。線程在這一狀態(tài)保持執(zhí)行,是一種忙等待。
避免了線程上下文的調(diào)度切換的開(kāi)銷,單核cpu不適合自旋鎖。
適用于鎖使用者保持鎖時(shí)間比較短的情況。自旋等待的時(shí)間必須要有一定的限度,如果自旋超過(guò)了限定次數(shù)還沒(méi)有成功獲得鎖,就會(huì)掛起線程。
可能會(huì)產(chǎn)生死鎖:同一線程兩次調(diào)用lock(),會(huì)使得第二次調(diào)用lock的位置進(jìn)行自旋,產(chǎn)生死鎖。
- 遞歸程序不能再持有自旋鎖時(shí)調(diào)用自己,也不能在遞歸調(diào)用時(shí)試圖去獲取相同的自旋鎖。
自旋鎖的實(shí)現(xiàn)原理同樣也是CAS,在AtomicInteger中調(diào)用Unsafe進(jìn)行自增操作時(shí)的while循環(huán)就是一個(gè)自旋操作,如果修改數(shù)值失敗就通過(guò)循環(huán)來(lái)執(zhí)行自旋,直到修改成功。
適應(yīng)性自旋鎖
自旋的時(shí)間(次數(shù))不再固定,由前一次在同一個(gè)鎖上的自旋時(shí)間以及鎖的擁有著狀態(tài)來(lái)決定自旋次數(shù)。
線程要不要鎖住同步資源:樂(lè)觀鎖和悲觀鎖
樂(lè)觀鎖
不鎖住同步資源。
對(duì)于同一個(gè)數(shù)據(jù)的并發(fā)操作,樂(lè)觀鎖認(rèn)為自己在使用數(shù)據(jù)時(shí)不會(huì)有別的線程修改數(shù)據(jù),所有不會(huì)添加鎖,只是在更新數(shù)據(jù)的時(shí)候再去判斷之前有沒(méi)有別的線程更新了這個(gè)數(shù)據(jù)。
如果數(shù)據(jù)沒(méi)有被更新,當(dāng)前線程成功寫(xiě)入自己的數(shù)據(jù);數(shù)據(jù)如果已經(jīng)被其他線程修改,根據(jù)不同的實(shí)現(xiàn)方法執(zhí)行不同的操作:報(bào)錯(cuò)或者自動(dòng)重試。
樂(lè)觀鎖可以做到不鎖定同步資源也可以正確的實(shí)現(xiàn)線程同步。
樂(lè)觀鎖在Java中的實(shí)現(xiàn):通過(guò)無(wú)鎖編程來(lái)實(shí)現(xiàn),最常采用CAS算法。
樂(lè)觀鎖適合讀操作多的場(chǎng)景,不加鎖的特點(diǎn)使讀操作的性能提升很多。
悲觀鎖
鎖住同步資源。
對(duì)于同一個(gè)數(shù)據(jù)的并發(fā)操作,悲觀鎖認(rèn)為自己在使用數(shù)據(jù)時(shí)一定有線程來(lái)修改數(shù)據(jù),因此在獲取數(shù)據(jù)時(shí)會(huì)先加鎖,以確保數(shù)據(jù)不會(huì)被別的線程所修改。
synchronized和Lock都是悲觀鎖。
悲觀鎖適合寫(xiě)操作多的場(chǎng)景,先加上鎖可以保證寫(xiě)操作時(shí)數(shù)據(jù)的正確性。
多個(gè)線程競(jìng)爭(zhēng)同步資源的流程細(xì)節(jié)有沒(méi)有區(qū)別?
無(wú)鎖、偏向鎖、輕量級(jí)鎖、重量級(jí)鎖這四種鎖是指鎖的狀態(tài),專門針對(duì)synchronized的。
無(wú)鎖
不鎖住資源,多個(gè)線程中只有一個(gè)能修改資源成功,其他線程會(huì)重試。
偏向鎖
同一個(gè)線程執(zhí)行同步資源時(shí)自動(dòng)獲取資源。
輕量級(jí)鎖
多個(gè)線程競(jìng)爭(zhēng)同步資源時(shí),沒(méi)有獲取資源的線程自旋等待鎖釋放。
重量級(jí)鎖
多個(gè)線程競(jìng)爭(zhēng)同步資源時(shí),沒(méi)有獲取資源的線程阻塞等待喚醒。
多個(gè)線程能不能共享一把鎖:共享鎖和排他鎖(獨(dú)享鎖)
共享鎖
鎖可以被多個(gè)線程所持有。
一個(gè)線程對(duì)一個(gè)對(duì)象加上共享鎖后,其他線程只能對(duì)這個(gè)對(duì)象再加共享鎖,不能再加排他鎖。
獲得共享鎖的線程只能讀數(shù)據(jù),不能寫(xiě)、修改數(shù)據(jù)。
11.排他鎖(獨(dú)享鎖、互斥鎖)
鎖一次只能被一個(gè)線程所擁有。
一個(gè)線程對(duì)一個(gè)對(duì)象加上排他鎖后,其他線程不能再對(duì)對(duì)象加任何類型的鎖。
獲得排他鎖的線程既可以讀數(shù)據(jù)又可以寫(xiě)數(shù)據(jù)。
ReentrantReadWriteLock中有兩把鎖:ReadLock(讀鎖)和WriteLock(寫(xiě)鎖),合稱為讀寫(xiě)鎖,讀鎖是共享鎖,寫(xiě)鎖是排他鎖。
讀寫(xiě)鎖將對(duì)一個(gè)資源文件的訪問(wèn)分為了讀鎖和寫(xiě)鎖。
讀鎖使得多個(gè)線程之間的讀操作不會(huì)發(fā)生沖突,讀鎖的共享鎖可以保證很高效的并發(fā)讀。
其他的鎖
- 可中斷鎖
可以相應(yīng)中斷的鎖。
- synchronized是不可中斷的鎖,Lock是可以中斷的鎖。
線程在等待獲取鎖時(shí)等待的時(shí)間過(guò)長(zhǎng),可以中斷自己或在別的線程中中斷它去處理其他的事情。
- 互斥鎖
Java內(nèi)置鎖稱為互斥鎖。沒(méi)有獲取到鎖的線程直接進(jìn)入阻塞狀態(tài)。
執(zhí)行流程:托管代碼-->用戶態(tài)代碼-->內(nèi)核態(tài)代碼。
采用互斥的方式,從沒(méi)有獲得鎖到獲得鎖的過(guò)程中,要有用戶態(tài)和內(nèi)核態(tài)調(diào)度和上下文切換的開(kāi)銷和損耗。