什么是重入鎖
我們提供的服務(wù)有:成都網(wǎng)站建設(shè)、網(wǎng)站設(shè)計(jì)、微信公眾號(hào)開(kāi)發(fā)、網(wǎng)站優(yōu)化、網(wǎng)站認(rèn)證、通道ssl等。為數(shù)千家企事業(yè)單位解決了網(wǎng)站和推廣的問(wèn)題。提供周到的售前咨詢和貼心的售后服務(wù),是有科學(xué)管理、有技術(shù)的通道網(wǎng)站制作公司
java.util.concurrent.locks.ReentrantLock
ReenTrantLock獨(dú)有的能力:
1.??????ReenTrantLock可以指定是公平鎖還是非公平鎖。而synchronized只能是非公平鎖。所謂的公平鎖就是先等待的線程先獲得鎖。
2.??????ReenTrantLock提供了一個(gè)Condition(條件)類,用來(lái)實(shí)現(xiàn)分組喚醒需要喚醒的線程們,而不是像synchronized要么隨機(jī)喚醒一個(gè)線程要么喚醒全部線程。
3.??????ReenTrantLock提供了一種能夠中斷等待鎖的線程的機(jī)制,通過(guò)lock.lockInterruptibly()來(lái)實(shí)現(xiàn)這個(gè)機(jī)制。
非重入鎖
當(dāng)A方法獲取鎖去鎖住一段需要做原子性操作的B方法時(shí),如果這段B方法又需要鎖去做原子性操作,那么A方法就必定要與B方法出現(xiàn)死鎖。這種會(huì)出現(xiàn)問(wèn)題的重入一把鎖的情況,叫不可重入鎖。
lock的過(guò)程:
首先嘗試獲取資源,如果當(dāng)前狀態(tài)為0,表示沒(méi)有線程占有鎖,設(shè)置該線程為獨(dú)占模式,使用CAS設(shè)置狀態(tài),否則如果當(dāng)前線程和獨(dú)占線程是一個(gè)線程,修改狀態(tài)值,否則返回false。
若獲取資源失敗,則通過(guò)addWaiter -(aqs)方法創(chuàng)建一個(gè)節(jié)點(diǎn)并放在CLH隊(duì)列的尾部。head tail未初始化會(huì)創(chuàng)建虛擬節(jié)點(diǎn)同時(shí)指向
為什么 AQS 需要一個(gè)虛擬 head 節(jié)點(diǎn)
每個(gè)節(jié)點(diǎn)都需要設(shè)置前置Node 的 waitStatus? 狀態(tài)(這個(gè)狀態(tài)為是為了保證數(shù)據(jù)一致性),防止重復(fù)釋放操作。而第一個(gè)節(jié)點(diǎn)是沒(méi)有前置節(jié)點(diǎn)的,所以需要?jiǎng)?chuàng)建一個(gè)虛擬節(jié)點(diǎn)。
逐步去執(zhí)行CLH隊(duì)列中的線程,當(dāng)前線程會(huì)公平性的阻塞一直到獲取鎖為止,返回線程在等待的過(guò)程中還是否中斷過(guò)。
unlock的過(guò)程
一次unlock操作需要修改狀態(tài)位,然后喚醒節(jié)點(diǎn)。整個(gè)釋放操作也是使用unpark()來(lái)喚醒隊(duì)列最前面的節(jié)點(diǎn)。其實(shí)lock中比較重要的也就是lock和release,它們又和AQS聯(lián)系緊密,下面會(huì)單獨(dú)談?wù)凙QS的重要方法。
Condition的await和signal
wait和notify/notify VS await和signal
Condition能夠支持不響應(yīng)中斷,而通過(guò)使用Object方式不支持;
Condition能夠支持多個(gè)等待隊(duì)列(new 多個(gè)Condition對(duì)象),而Object方式只能支持一個(gè);
Condition能夠支持超時(shí)時(shí)間的設(shè)置,而Object不支持
對(duì)標(biāo)Object的wait方法
void await() throws InterruptedException:當(dāng)前線程進(jìn)入等待狀態(tài),如果其他線程調(diào)用condition的signal或者signalAll方法并且當(dāng)前線程獲取Lock從await方法返回,如果在等待狀態(tài)中被中斷會(huì)拋出被中斷異常;
long awaitNanos(long nanosTimeout):當(dāng)前線程進(jìn)入等待狀態(tài)直到被通知,中斷或者超時(shí);
boolean await(long time, TimeUnit unit)throws InterruptedException:同第二種,支持自定義時(shí)間單位
boolean awaitUntil(Date deadline) throws InterruptedException:當(dāng)前線程進(jìn)入等待狀態(tài)直到被通知,中斷或者到了某個(gè)時(shí)間
對(duì)標(biāo)Object的notify/notifyAll方法
void signal():?jiǎn)拘岩粋€(gè)等待在condition上的線程,將該線程從等待隊(duì)列中轉(zhuǎn)移到同步隊(duì)列中,如果在同步隊(duì)列中能夠競(jìng)爭(zhēng)到Lock則可以從等待方法中返回。
void signalAll():與1的區(qū)別在于能夠喚醒所有等待在condition上的線程
如圖所示,ConditionObject是AQS的內(nèi)部類,因此每個(gè)ConditionObject能夠訪問(wèn)到AQS提供的方法,相當(dāng)于每個(gè)Condition都擁有所屬同步器的引用。
調(diào)用condition.await方法的線程必須是已經(jīng)獲得了lock,也就是當(dāng)前線程是同步隊(duì)列中的頭結(jié)點(diǎn)。調(diào)用該方法后會(huì)使得當(dāng)前線程所封裝的Node尾插入到等待隊(duì)列中。
如圖,線程awaitThread先通過(guò)lock.lock()方法獲取鎖成功后調(diào)用了condition.await方法進(jìn)入等待隊(duì)列,而另一個(gè)線程signalThread通過(guò)lock.lock()方法獲取鎖成功后調(diào)用了condition.signal或者signalAll方法,使得線程awaitThread能夠有機(jī)會(huì)移入到同步隊(duì)列中,當(dāng)其他線程釋放lock后使得線程awaitThread能夠有機(jī)會(huì)獲取lock,從而使得線程awaitThread能夠從await方法中退出執(zhí)行后續(xù)操作。如果awaitThread獲取lock失敗會(huì)直接進(jìn)入到同步隊(duì)列。
// 線程已被取消
static final int CANCELLED =? 1;
// 當(dāng)前線程的后繼線程需要被unpark(喚醒)
// 一般發(fā)生情況是:當(dāng)前線程的后繼線程處于阻塞狀態(tài),而當(dāng)前線程被release或cancel掉,因此需要喚醒當(dāng)前線程的后繼線程。
static final int SIGNAL? ? = -1;
// 在Condition休眠狀態(tài),在等待Condition喚醒
static final int CONDITION = -2;
// (共享鎖)其它線程獲取到“共享鎖”,對(duì)應(yīng)的waitStatus的值
static final int PROPAGATE = -3;
volatile int waitStatus;
---------------------
/**
* 這個(gè)方法也就是lock()方法的關(guān)鍵方法。tryAcquire獲得資源,返回true,直接結(jié)束。若未獲取資源,新建一個(gè)節(jié)點(diǎn)插入隊(duì)尾,
*addWaiter用于添加節(jié)點(diǎn),也就是把當(dāng)前線程對(duì)應(yīng)的節(jié)點(diǎn)插入CLH隊(duì)列的尾部。
* @param arg the acquire argument.? This value is conveyed to
*? ? ? ? {@link #tryAcquire} but is otherwise uninterpreted and
*? ? ? ? can represent anything you like.
*/
public final void acquire(int arg) {
? ? if (!tryAcquire(arg) //獲取資源立刻結(jié)束
? ? ? ? acquireQueued(addWaiter(Node.EXCLUSIVE), arg))//沒(méi)有被中斷過(guò),也結(jié)束
? ? ? ? selfInterrupt();
}
---------------------
protected final boolean tryAcquire(int acquires) {
? ? ? ? final Thread current = Thread.currentThread();
? ? ? ? int c = getState();
? ? ? ? if (c == 0) {
? ? ? ? ? ? if (!hasQueuedPredecessors()
? ? ? ? ? ? ? ? compareAndSetState(0, acquires)) {
? ? ? ? ? ? ? ? setExclusiveOwnerThread(current);
? ? ? ? ? ? ? ? return true;
? ? ? ? ? ? }
? ? ? ? }
? ? ? ? else if (current == getExclusiveOwnerThread()) { //判斷是否持有鎖的是自己,重入
? ? ? ? ? ? int nextc = c + acquires;
? ? ? ? ? ? if (nextc 0)
? ? ? ? ? ? ? ? throw new Error("Maximum lock count exceeded");
? ? ? ? ? ? setState(nextc);
? ? ? ? ? ? return true;
? ? ? ? }
? ? ? ? return false;
? ? }
---------------------
* 非公平鎖
*/
static final class NonfairSync extends Sync {
? ? private static final long serialVersionUID = 7316153563782823691L;
? ? /**
? ? * Performs lock.? Try immediate barge, backing up to normal
? ? * acquire on failure.
? ? */
? ? final void lock() {
? ? ? ? if (compareAndSetState(0, 1))//CAS設(shè)置當(dāng)前為0 的時(shí)候上鎖
? ? ? ? ? ? setExclusiveOwnerThread(Thread.currentThread());
? ? ? ? else
? ? ? ? ? ? acquire(1);//否則嘗試獲得鎖。
? ? }
? ? protected final boolean tryAcquire(int acquires) {
? ? ? ? return nonfairTryAcquire(acquires);
? ? }
}
/**
* 公平鎖
*/
static final class FairSync extends Sync {
? ? private static final long serialVersionUID = -3000897897090466540L;
? ? final void lock() {
? ? ? ? acquire(1);
? ? }
? ? /**
? ? *
? ? */
? ? protected final boolean tryAcquire(int acquires) {
? ? ? ? final Thread current = Thread.currentThread();
? ? ? ? int c = getState();
? ? ? ? if (c == 0) {
? ? ? ? ? ? if (!hasQueuedPredecessors()
? ? ? ? ? ? ? ? compareAndSetState(0, acquires)) {//沒(méi)有前驅(qū)節(jié)點(diǎn)并且CAS設(shè)置成功
? ? ? ? ? ? ? ? setExclusiveOwnerThread(current);//設(shè)置當(dāng)前線程為獨(dú)占線程
? ? ? ? ? ? ? ? return true;
? ? ? ? ? ? }
? ? ? ? }
? ? ? ? else if (current == getExclusiveOwnerThread()) {//這里和非公平鎖類似
? ? ? ? ? ? int nextc = c + acquires;
? ? ? ? ? ? if (nextc 0)
? ? ? ? ? ? ? ? throw new Error("Maximum lock count exceeded");
? ? ? ? ? ? setState(nextc);
? ? ? ? ? ? return true;
? ? ? ? }
? ? ? ? return false;
? ? }
}
讀寫鎖 ReadWriteLock讀寫鎖維護(hù)了一對(duì)相關(guān)的鎖,一個(gè)用于只讀操作,一個(gè)用于寫入操作。只要沒(méi)有writer,讀取鎖可以由多個(gè)reader線程同時(shí)保持。寫入鎖是獨(dú)占的。
互斥鎖一次只允許一個(gè)線程訪問(wèn)共享數(shù)據(jù),哪怕進(jìn)行的是只讀操作;讀寫鎖允許對(duì)共享數(shù)據(jù)進(jìn)行更高級(jí)別的并發(fā)訪問(wèn):對(duì)于寫操作,一次只有一個(gè)線程(write線程)可以修改共享數(shù)據(jù),對(duì)于讀操作,允許任意數(shù)量的線程同時(shí)進(jìn)行讀取。
與互斥鎖相比,使用讀寫鎖能否提升性能則取決于讀寫操作期間讀取數(shù)據(jù)相對(duì)于修改數(shù)據(jù)的頻率,以及數(shù)據(jù)的爭(zhēng)用——即在同一時(shí)間試圖對(duì)該數(shù)據(jù)執(zhí)行讀取或?qū)懭氩僮鞯木€程數(shù)。
讀寫鎖適用于讀多寫少的情況。
可重入讀寫鎖 ReentrantReadWriteLock
屬性ReentrantReadWriteLock 也是基于 AbstractQueuedSynchronizer 實(shí)現(xiàn)的,它具有下面這些屬性(來(lái)自Java doc文檔):
* 獲取順序:此類不會(huì)將讀取者優(yōu)先或?qū)懭胝邇?yōu)先強(qiáng)加給鎖訪問(wèn)的排序。
* 非公平模式(默認(rèn)):連續(xù)競(jìng)爭(zhēng)的非公平鎖可能無(wú)限期地推遲一個(gè)或多個(gè)reader或writer線程,但吞吐量通常要高于公平鎖。
* 公平模式:線程利用一個(gè)近似到達(dá)順序的策略來(lái)爭(zhēng)奪進(jìn)入。當(dāng)釋放當(dāng)前保持的鎖時(shí),可以為等待時(shí)間最長(zhǎng)的單個(gè)writer線程分配寫入鎖,如果有一組等待時(shí)間大于所有正在等待的writer線程的reader,將為該組分配讀者鎖。
* 試圖獲得公平寫入鎖的非重入的線程將會(huì)阻塞,除非讀取鎖和寫入鎖都自由(這意味著沒(méi)有等待線程)。
* 重入:此鎖允許reader和writer按照 ReentrantLock 的樣式重新獲取讀取鎖或?qū)懭腈i。在寫入線程保持的所有寫入鎖都已經(jīng)釋放后,才允許重入reader使用讀取鎖。
writer可以獲取讀取鎖,但reader不能獲取寫入鎖。
* 鎖降級(jí):重入還允許從寫入鎖降級(jí)為讀取鎖,實(shí)現(xiàn)方式是:先獲取寫入鎖,然后獲取讀取鎖,最后釋放寫入鎖。但是,從讀取鎖升級(jí)到寫入鎖是不可能的。
* 鎖獲取的中斷:讀取鎖和寫入鎖都支持鎖獲取期間的中斷。
* Condition 支持:寫入鎖提供了一個(gè) Condition 實(shí)現(xiàn),對(duì)于寫入鎖來(lái)說(shuō),該實(shí)現(xiàn)的行為與 ReentrantLock.newCondition() 提供的Condition 實(shí)現(xiàn)對(duì) ReentrantLock 所做的行為相同。當(dāng)然,此 Condition 只能用于寫入鎖。
讀取鎖不支持 Condition,readLock().newCondition() 會(huì)拋出 UnsupportedOperationException。
* 監(jiān)測(cè):此類支持一些確定是讀取鎖還是寫入鎖的方法。這些方法設(shè)計(jì)用于監(jiān)視系統(tǒng)狀態(tài),而不是同步控制。
實(shí)現(xiàn)AQS 回顧在之前的文章已經(jīng)提到,AQS以單個(gè) int 類型的原子變量來(lái)表示其狀態(tài),定義了4個(gè)抽象方法( tryAcquire(int)、tryRelease(int)、tryAcquireShared(int)、tryReleaseShared(int),前兩個(gè)方法用于獨(dú)占/排他模式,后兩個(gè)用于共享模式 )留給子類實(shí)現(xiàn),用于自定義同步器的行為以實(shí)現(xiàn)特定的功能。
對(duì)于 ReentrantLock,它是可重入的獨(dú)占鎖,內(nèi)部的 Sync 類實(shí)現(xiàn)了 tryAcquire(int)、tryRelease(int) 方法,并用狀態(tài)的值來(lái)表示重入次數(shù),加鎖或重入鎖時(shí)狀態(tài)加 1,釋放鎖時(shí)狀態(tài)減 1,狀態(tài)值等于 0 表示鎖空閑。
對(duì)于 CountDownLatch,它是一個(gè)關(guān)卡,在條件滿足前阻塞所有等待線程,條件滿足后允許所有線程通過(guò)。內(nèi)部類 Sync 把狀態(tài)初始化為大于 0 的某個(gè)值,當(dāng)狀態(tài)大于 0 時(shí)所有wait線程阻塞,每調(diào)用一次 countDown 方法就把狀態(tài)值減 1,減為 0 時(shí)允許所有線程通過(guò)。利用了AQS的共享模式。
現(xiàn)在,要用AQS來(lái)實(shí)現(xiàn) ReentrantReadWriteLock。
一點(diǎn)思考問(wèn)題
* AQS只有一個(gè)狀態(tài),那么如何表示 多個(gè)讀鎖 與 單個(gè)寫鎖 呢?
* ReentrantLock 里,狀態(tài)值表示重入計(jì)數(shù),現(xiàn)在如何在AQS里表示每個(gè)讀鎖、寫鎖的重入次數(shù)呢?
* 如何實(shí)現(xiàn)讀鎖、寫鎖的公平性呢?
常見(jiàn)的Java鎖有下面這些:
公平鎖/非公平鎖
可重入鎖
獨(dú)享鎖/共享鎖
互斥鎖/讀寫鎖
樂(lè)觀鎖/悲觀鎖
分段鎖
偏向鎖/輕量級(jí)鎖/重量級(jí)鎖
自旋鎖
這些分類并不是全是指鎖的狀態(tài),有的指鎖的特性,有的指鎖的設(shè)計(jì),下面總結(jié)的內(nèi)容是對(duì)每個(gè)鎖的名詞進(jìn)行一定的解釋。
公平鎖/非公平鎖
公平鎖是指多個(gè)線程按照申請(qǐng)鎖的順序來(lái)獲取鎖。
非公平鎖是指多個(gè)線程獲取鎖的順序并不是按照申請(qǐng)鎖的順序,有可能后申請(qǐng)的線程比先申請(qǐng)的線程優(yōu)先獲取鎖。有可能,會(huì)造成優(yōu)先級(jí)反轉(zhuǎn)或者饑餓現(xiàn)象。
對(duì)于Java?ReentrantLock而言,通過(guò)構(gòu)造函數(shù)指定該鎖是否是公平鎖,默認(rèn)是非公平鎖。非公平鎖的優(yōu)點(diǎn)在于吞吐量比公平鎖大。
對(duì)于Synchronized而言,也是一種非公平鎖。由于其并不像ReentrantLock是通過(guò)AQS的來(lái)實(shí)現(xiàn)線程調(diào)度,所以并沒(méi)有任何辦法使其變成公平鎖。
可重入鎖
可重入鎖又名遞歸鎖,是指在同一個(gè)線程在外層方法獲取鎖的時(shí)候,在進(jìn)入內(nèi)層方法會(huì)自動(dòng)獲取鎖。說(shuō)的有點(diǎn)抽象,下面會(huì)有一個(gè)代碼的示例。
對(duì)于Java?ReentrantLock而言, 他的名字就可以看出是一個(gè)可重入鎖,其名字是Re entrant Lock重新進(jìn)入鎖。
對(duì)于Synchronized而言,也是一個(gè)可重入鎖??芍厝腈i的一個(gè)好處是可一定程度避免死鎖。
synchronized void setA() throws Exception{
Thread.sleep(1000);
setB();
}synchronized void setB() throws Exception{
Thread.sleep(1000);
}
上面的代碼就是一個(gè)可重入鎖的一個(gè)特點(diǎn),如果不是可重入鎖的話,setB可能不會(huì)被當(dāng)前線程執(zhí)行,可能造成死鎖。
獨(dú)享鎖/共享鎖
獨(dú)享鎖是指該鎖一次只能被一個(gè)線程所持有。
共享鎖是指該鎖可被多個(gè)線程所持有。
對(duì)于Java?ReentrantLock而言,其是獨(dú)享鎖。但是對(duì)于Lock的另一個(gè)實(shí)現(xiàn)類ReadWriteLock,其讀鎖是共享鎖,其寫鎖是獨(dú)享鎖。
讀鎖的共享鎖可保證并發(fā)讀是非常高效的,讀寫,寫讀 ,寫寫的過(guò)程是互斥的。
獨(dú)享鎖與共享鎖也是通過(guò)AQS來(lái)實(shí)現(xiàn)的,通過(guò)實(shí)現(xiàn)不同的方法,來(lái)實(shí)現(xiàn)獨(dú)享或者共享。
對(duì)于Synchronized而言,當(dāng)然是獨(dú)享鎖。
互斥鎖/讀寫鎖
上面講的獨(dú)享鎖/共享鎖就是一種廣義的說(shuō)法,互斥鎖/讀寫鎖就是具體的實(shí)現(xiàn)。
互斥鎖在Java中的具體實(shí)現(xiàn)就是ReentrantLock
讀寫鎖在Java中的具體實(shí)現(xiàn)就是ReadWriteLock
樂(lè)觀鎖/悲觀鎖
樂(lè)觀鎖與悲觀鎖不是指具體的什么類型的鎖,而是指看待并發(fā)同步的角度。
悲觀鎖認(rèn)為對(duì)于同一個(gè)數(shù)據(jù)的并發(fā)操作,一定是會(huì)發(fā)生修改的,哪怕沒(méi)有修改,也會(huì)認(rèn)為修改。因此對(duì)于同一個(gè)數(shù)據(jù)的并發(fā)操作,悲觀鎖采取加鎖的形式。悲觀的認(rèn)為,不加鎖的并發(fā)操作一定會(huì)出問(wèn)題。
樂(lè)觀鎖則認(rèn)為對(duì)于同一個(gè)數(shù)據(jù)的并發(fā)操作,是不會(huì)發(fā)生修改的。在更新數(shù)據(jù)的時(shí)候,會(huì)采用嘗試更新,不斷重新的方式更新數(shù)據(jù)。樂(lè)觀的認(rèn)為,不加鎖的并發(fā)操作是沒(méi)有事情的。
從上面的描述我們可以看出,悲觀鎖適合寫操作非常多的場(chǎng)景,樂(lè)觀鎖適合讀操作非常多的場(chǎng)景,不加鎖會(huì)帶來(lái)大量的性能提升。
悲觀鎖在Java中的使用,就是利用各種鎖。
樂(lè)觀鎖在Java中的使用,是無(wú)鎖編程,常常采用的是CAS算法,典型的例子就是原子類,通過(guò)CAS自旋實(shí)現(xiàn)原子操作的更新。
分段鎖
分段鎖其實(shí)是一種鎖的設(shè)計(jì),并不是具體的一種鎖,對(duì)于ConcurrentHashMap而言,其并發(fā)的實(shí)現(xiàn)就是通過(guò)分段鎖的形式來(lái)實(shí)現(xiàn)高效的并發(fā)操作。
我們以ConcurrentHashMap來(lái)說(shuō)一下分段鎖的含義以及設(shè)計(jì)思想,ConcurrentHashMap中的分段鎖稱為Segment,它即類似于HashMap(JDK7與JDK8中HashMap的實(shí)現(xiàn))的結(jié)構(gòu),即內(nèi)部擁有一個(gè)Entry數(shù)組,數(shù)組中的每個(gè)元素又是一個(gè)鏈表;同時(shí)又是一個(gè)ReentrantLock(Segment繼承了ReentrantLock)。
當(dāng)需要put元素的時(shí)候,并不是對(duì)整個(gè)hashmap進(jìn)行加鎖,而是先通過(guò)hashcode來(lái)知道他要放在那一個(gè)分段中,然后對(duì)這個(gè)分段進(jìn)行加鎖,所以當(dāng)多線程put的時(shí)候,只要不是放在一個(gè)分段中,就實(shí)現(xiàn)了真正的并行的插入。
但是,在統(tǒng)計(jì)size的時(shí)候,可就是獲取hashmap全局信息的時(shí)候,就需要獲取所有的分段鎖才能統(tǒng)計(jì)。
分段鎖的設(shè)計(jì)目的是細(xì)化鎖的粒度,當(dāng)操作不需要更新整個(gè)數(shù)組的時(shí)候,就僅僅針對(duì)數(shù)組中的一項(xiàng)進(jìn)行加鎖操作。
偏向鎖/輕量級(jí)鎖/重量級(jí)鎖
這三種鎖是指鎖的狀態(tài),并且是針對(duì)Synchronized。在Java 5通過(guò)引入鎖升級(jí)的機(jī)制來(lái)實(shí)現(xiàn)高效Synchronized。這三種鎖的狀態(tài)是通過(guò)對(duì)象監(jiān)視器在對(duì)象頭中的字段來(lái)表明的。
偏向鎖是指一段同步代碼一直被一個(gè)線程所訪問(wèn),那么該線程會(huì)自動(dòng)獲取鎖。降低獲取鎖的代價(jià)。
輕量級(jí)鎖是指當(dāng)鎖是偏向鎖的時(shí)候,被另一個(gè)線程所訪問(wèn),偏向鎖就會(huì)升級(jí)為輕量級(jí)鎖,其他線程會(huì)通過(guò)自旋的形式嘗試獲取鎖,不會(huì)阻塞,提高性能。
重量級(jí)鎖是指當(dāng)鎖為輕量級(jí)鎖的時(shí)候,另一個(gè)線程雖然是自旋,但自旋不會(huì)一直持續(xù)下去,當(dāng)自旋一定次數(shù)的時(shí)候,還沒(méi)有獲取到鎖,就會(huì)進(jìn)入阻塞,該鎖膨脹為重量級(jí)鎖。重量級(jí)鎖會(huì)讓其他申請(qǐng)的線程進(jìn)入阻塞,性能降低。
自旋鎖
在Java中,自旋鎖是指嘗試獲取鎖的線程不會(huì)立即阻塞,而是采用循環(huán)的方式去嘗試獲取鎖,這樣的好處是減少線程上下文切換的消耗,缺點(diǎn)是循環(huán)會(huì)消耗CPU。
一、synchronized和lock的用法區(qū)別
synchronized:在需要同步的對(duì)象中加入此控制,synchronized在方法上,也在特定代碼塊中,括號(hào)中表示需要鎖的對(duì)象。
lock:需要顯示指定起始位置和終止位置。一般使用ReentrantLock類做為鎖,多個(gè)線程中必須要使用一個(gè)ReentrantLock類做為對(duì)象才能保證鎖的生效。且在加鎖和解鎖處需要通過(guò)lock()和unlock()顯示指出。所以一般會(huì)在finally塊中寫unlock()以防死鎖。
二、synchronized和lock用途區(qū)別
synchronized原語(yǔ)和ReentrantLock在一般情況下沒(méi)有什么區(qū)別,但是在非常復(fù)雜的同步應(yīng)用中,請(qǐng)考慮使用ReentrantLock,特別是遇到下面2種需求的時(shí)候。
某個(gè)線程在等待一個(gè)鎖的控制權(quán)的這段時(shí)間需要中斷
2.需要分開(kāi)處理一些wait-notify,ReentrantLock里面的Condition應(yīng)用,能夠控制notify哪個(gè)線程
3.具有公平鎖功能,每個(gè)到來(lái)的線程都將排隊(duì)等候