這篇文章主要講解了“有了Synchronized還需要Lock的原因是什么”,文中的講解內(nèi)容簡(jiǎn)單清晰,易于學(xué)習(xí)與理解,下面請(qǐng)大家跟著小編的思路慢慢深入,一起來(lái)研究和學(xué)習(xí)“有了Synchronized還需要Lock的原因是什么”吧!
創(chuàng)新互聯(lián)專(zhuān)注于輪臺(tái)企業(yè)網(wǎng)站建設(shè),響應(yīng)式網(wǎng)站建設(shè),商城系統(tǒng)網(wǎng)站開(kāi)發(fā)。輪臺(tái)網(wǎng)站建設(shè)公司,為輪臺(tái)等地區(qū)提供建站服務(wù)。全流程按需策劃設(shè)計(jì),專(zhuān)業(yè)設(shè)計(jì),全程項(xiàng)目跟蹤,創(chuàng)新互聯(lián)專(zhuān)業(yè)和態(tài)度為您提供的服務(wù)
在并發(fā)編程領(lǐng)域,有兩大核心問(wèn)題:互斥、同步
互斥:指的是同一時(shí)刻只允許一個(gè)線程訪問(wèn)共享資源。
同步:指的是線程之間的通信和協(xié)作。
這兩大問(wèn)題用管程(monitor,是操作系統(tǒng)中的一個(gè)重要概念)都能解決。
Java 關(guān)鍵字Synchronized 已經(jīng)是管程的一個(gè)實(shí)現(xiàn)了,那為什么Java SDK并發(fā)包還需要搞一個(gè)Lock來(lái)實(shí)現(xiàn)管程呢?這不是重復(fù)了嘛?
我們先來(lái)看下死鎖產(chǎn)生的四個(gè)必要條件:
1、互斥:某共享資源一次只允許一個(gè)線程占有。
2、占有且等待:一個(gè)線程本身占有資源(一種或多種),同時(shí)還有資源未得到滿(mǎn)足,則不會(huì)釋放已有的資源。
3、不可搶占:其他線程不能搶占已被別的線程占有的資源。
4、循環(huán)等待:線程A等待線程B的資源,線程B等待線程A的資源,這就是循環(huán)等待。
只要這4個(gè)條件都滿(mǎn)足就會(huì)發(fā)生死鎖,也就是說(shuō)這4個(gè)條件我們只要破環(huán)一個(gè)就不會(huì)發(fā)生死鎖,然而Synchronized無(wú)法破環(huán)不可搶占條件,因?yàn)镾ynchronized申請(qǐng)資源的時(shí)候,如果申請(qǐng)不到,線程就直接進(jìn)入阻塞狀態(tài),進(jìn)入阻塞狀態(tài)就啥事都干不了了,所以也釋放不了已經(jīng)占有的資源。
所以就搞了個(gè)Lock。Lock搞了3種方法來(lái)破環(huán)不可搶占的條件。
1、void lockInterruptibly() throws InterruptedException;
這是個(gè)支持中斷的API。Synchronized進(jìn)入阻塞之后就沒(méi)辦法喚醒它,所以針對(duì)這個(gè)問(wèn)題想了個(gè)支持響應(yīng)中斷的方法,讓線程阻塞(lock下是等待狀態(tài))的時(shí)候可以響應(yīng)中斷信號(hào),從而有機(jī)會(huì)釋放已占有的資源來(lái)破環(huán)不可搶占的條件。
2、boolean tryLock();
這就是在獲取鎖的時(shí)候,如果獲取不到就直接返回,這樣也有機(jī)會(huì)釋放已占有的資源來(lái)破環(huán)不可搶占的條件。
3、boolean tryLock(long time, TimeUnit unit) throws InterrptedException;
這是個(gè)支持超時(shí)的API,也就是讓在一段時(shí)間內(nèi)獲取不到資源的線程直接返回一個(gè)錯(cuò)誤,不進(jìn)入阻塞狀態(tài),那也有機(jī)會(huì)釋放已占有的資源來(lái)破環(huán)不可搶占的條件。
然后再來(lái)說(shuō)說(shuō)Lock的實(shí)現(xiàn)類(lèi)一共有三個(gè),一個(gè)是ReentrantLock,另兩個(gè)是ReentrantReadWriteLock類(lèi)中的兩個(gè)靜態(tài)內(nèi)部類(lèi)ReadLock和WriteLock。實(shí)現(xiàn)思路大同小異。
我來(lái)簡(jiǎn)單說(shuō)說(shuō)ReentrantLock,它是利用了volatile 的Happens-Before規(guī)則來(lái)保證了可見(jiàn)性,ReentrantLock內(nèi)部持有一個(gè)volatile成員變量state,每次獲取鎖的時(shí)候會(huì)讀寫(xiě)state的值,每次解鎖的時(shí)候也會(huì)讀寫(xiě)state的值??聪驴s減的示意代碼
volatile int state;
lock() {
state = 1;
}
unlock() {
state = 0;
}
volatile 變量規(guī)則:對(duì)volatile變量的寫(xiě)操作Happens-Before 于后面對(duì)這個(gè)變量的讀操作。
來(lái)看個(gè)例子:
private final Lock lock = new ReentrantLock();
int a;
public void addOne() {
lock.lock();
try{
a = getA()+1;
} finally {
lock.unlock();
}
}
public int getA() {
lock.lock();
try{
return a;
} finally {
lock.unlock();
}
}
假設(shè)現(xiàn)在有兩個(gè)線程A和B
1、根據(jù)程序次序規(guī)則:線程A中a = getA()+1;
Happens-Before 于lock.unlock();
2、根據(jù)volatile 變量規(guī)則:來(lái)看lock方法,state=1
,它首先是會(huì)讀取state的值,而unlock執(zhí)行state=0
,對(duì)state進(jìn)行了寫(xiě)操作。所以線程A執(zhí)行了unlock操作,那么根據(jù)volatile 變量規(guī)則,它是Happens-Before 線程B的lock操作。
3、根據(jù)傳遞性規(guī)則:所以線程A中a = getA()+1;
Happens-Before線程B的lock操作。所以線程A中加一之后結(jié)果是對(duì)線程B可見(jiàn)的!
感謝各位的閱讀,以上就是“有了Synchronized還需要Lock的原因是什么”的內(nèi)容了,經(jīng)過(guò)本文的學(xué)習(xí)后,相信大家對(duì)有了Synchronized還需要Lock的原因是什么這一問(wèn)題有了更深刻的體會(huì),具體使用情況還需要大家實(shí)踐驗(yàn)證。這里是創(chuàng)新互聯(lián),小編將為大家推送更多相關(guān)知識(shí)點(diǎn)的文章,歡迎關(guān)注!