成人无码视频,亚洲精品久久久久av无码,午夜精品久久久久久毛片,亚洲 中文字幕 日韩 无码

資訊專欄INFORMATION COLUMN

線程間的同步與通信(4)——Lock 和 Condtion

Aceyclee / 3046人閱讀

摘要:為了拓展同步代碼塊中的監(jiān)視器鎖,開(kāi)始,出現(xiàn)了接口,它實(shí)現(xiàn)了可定時(shí)可輪詢與可中斷的鎖獲取操作,公平隊(duì)列,以及非塊結(jié)構(gòu)的鎖。

前言

系列文章目錄

前面幾篇我們學(xué)習(xí)了synchronized同步代碼塊,了解了java的內(nèi)置鎖,并學(xué)習(xí)了監(jiān)視器鎖的wait/notify機(jī)制。在大多數(shù)情況下,內(nèi)置鎖都能很好的工作,但它在功能上存在一些局限性,例如無(wú)法實(shí)現(xiàn)非阻塞結(jié)構(gòu)的加鎖規(guī)則等。為了拓展同步代碼塊中的監(jiān)視器鎖,java 1.5 開(kāi)始,出現(xiàn)了lock接口,它實(shí)現(xiàn)了可定時(shí)、可輪詢與可中斷的鎖獲取操作,公平隊(duì)列,以及非塊結(jié)構(gòu)的鎖。

與內(nèi)置鎖不同,Lock是一種顯式鎖,它更加“危險(xiǎn)”,因?yàn)樵诔绦螂x開(kāi)被鎖保護(hù)的代碼塊時(shí),不會(huì)像監(jiān)視器鎖那樣自動(dòng)釋放,需要我們手動(dòng)釋放鎖。所以,在我們使用lock鎖時(shí),一定要記得:
在finally塊中調(diào)用lock.unlock()手動(dòng)釋放鎖!??!
在finally塊中調(diào)用lock.unlock()手動(dòng)釋放鎖!?。?/em>
在finally塊中調(diào)用lock.unlock()手動(dòng)釋放鎖?。?!

Lock接口
public interface Lock {
    void lock();
    void lockInterruptibly() throws InterruptedException;
    
    boolean tryLock();
    boolean tryLock(long time, TimeUnit unit) throws InterruptedException;

    void unlock();
    
    Condition newCondition();
}

典型的使用方式:

Lock l = ...;
l.lock();
try {
    // access the resource protected by this lock
} finally {
    l.unlock();
}
鎖的獲取

Lock接口定義了四種獲取鎖的方式,下面我們一個(gè)個(gè)來(lái)看

lock()

阻塞式獲取,在沒(méi)有獲取到鎖時(shí),當(dāng)前線程將會(huì)休眠,不會(huì)參與線程調(diào)度,直到獲取到鎖為止,獲取鎖的過(guò)程中不響應(yīng)中斷

lockInterruptibly()

阻塞式獲取,并且可中斷,該方法將在以下兩種情況之一發(fā)生的情況下拋出InterruptedException

在調(diào)用該方法時(shí),線程的中斷標(biāo)志位已經(jīng)被設(shè)為true了

在獲取鎖的過(guò)程中,線程被中斷了,并且鎖的獲取實(shí)現(xiàn)會(huì)響應(yīng)這個(gè)中斷

在InterruptedException拋出后,當(dāng)前線程的中斷標(biāo)志位將會(huì)被清除

tryLock()

非阻塞式獲取,從名字中也可以看出,try就是試一試的意思,無(wú)論成功與否,該方法都是立即返回的

相比前面兩種阻塞式獲取的方式,該方法是有返回值的,獲取鎖成功了則返回true,獲取鎖失敗了則返回false

tryLock(long time, TimeUnit unit)

帶超時(shí)機(jī)制,并且可中斷

如果可以獲取帶鎖,則立即返回true

如果獲取不到鎖,則當(dāng)前線程將會(huì)休眠,不會(huì)參與線程調(diào)度,直到以下三個(gè)條件之一被滿足:

當(dāng)前線程獲取到了鎖

其它線程中斷了當(dāng)前線程

設(shè)定的超時(shí)時(shí)間到了

該方法將在以下兩種情況之一發(fā)生的情況下拋出InterruptedException

在調(diào)用該方法時(shí),線程的中斷標(biāo)志位已經(jīng)被設(shè)為true了

在獲取鎖的過(guò)程中,線程被中斷了,并且鎖的獲取實(shí)現(xiàn)會(huì)響應(yīng)這個(gè)中斷

在InterruptedException拋出后,當(dāng)前線程的中斷標(biāo)志位將會(huì)被清除

如果超時(shí)時(shí)間到了,當(dāng)前線程還沒(méi)有獲得鎖,則會(huì)直接返回false(注意,這里并沒(méi)有拋出超時(shí)異常)

其實(shí),tryLock(long time, TimeUnit unit)更像是阻塞式與非阻塞式的結(jié)合體,即在一定條件下(超時(shí)時(shí)間內(nèi),沒(méi)有中斷發(fā)生)阻塞,不滿足這個(gè)條件則立即返回(非阻塞)。

這里把四種鎖的獲取方式總結(jié)如下:

鎖的釋放

相對(duì)于鎖的獲取,鎖的釋放的方法就簡(jiǎn)單的多,只有一個(gè)

void unlock();

值得注意的是,只有擁有的鎖的線程才能釋放鎖,并且,必須顯式地釋放鎖,這一點(diǎn)和離開(kāi)同步代碼塊就自動(dòng)被釋放的監(jiān)視器鎖是不同的。

newCondition

Lock接口還定義了一個(gè)newCondition方法:

Condition newCondition();

該方法將創(chuàng)建一個(gè)綁定在當(dāng)前Lock對(duì)象上的Condition對(duì)象,這說(shuō)明Condition對(duì)象和Lock對(duì)象是對(duì)應(yīng)的,一個(gè)Lock對(duì)象可以創(chuàng)建多個(gè)Condition對(duì)象,它們是一個(gè)對(duì)多的關(guān)系。

Condition 接口

上面我們說(shuō)道,Lock接口中定義了newCondition方法,它返回一個(gè)關(guān)聯(lián)在當(dāng)前Lock對(duì)象上的Condition對(duì)象,下面我們來(lái)看看這個(gè)Condition對(duì)象是個(gè)啥。

每一個(gè)新工具的出現(xiàn)總是為了解決一定的問(wèn)題,Condition接口的出現(xiàn)也不例外。
如果說(shuō)Lock接口的出現(xiàn)是為了拓展現(xiàn)有的監(jiān)視器鎖,那么Condition接口的出現(xiàn)就是為了拓展同步代碼塊中的wait, notify機(jī)制。

監(jiān)視器鎖的 wait/notify 機(jī)制的弊端

通常情況下,我們調(diào)用wait方法,主要是因?yàn)橐欢ǖ臈l件沒(méi)有滿足,我們把需要滿足的事件或條件稱作條件謂詞。

而另一方面,由前面幾篇介紹synchronized原理的文章我們知道,所有調(diào)用了wait方法的線程,都會(huì)在同一個(gè)監(jiān)視器鎖的wait set中等待,這看上去很合理,但是卻是該機(jī)制的短板所在——所有的線程都等待在同一個(gè)notify方法上(notify方法指notify()notifyAll()兩個(gè)方法,下同)。每一個(gè)調(diào)用wait方法的線程可能等待在不同的條件謂詞上,但是有時(shí)候即使自己等待的條件并沒(méi)有滿足,線程也有可能被“別的線程的”notify方法喚醒,因?yàn)榇蠹矣玫氖峭粋€(gè)監(jiān)視器鎖。這就好比一個(gè)班上有幾個(gè)重名的同學(xué)(使用相同的監(jiān)視器鎖),老師喊了這個(gè)名字(notify方法),結(jié)果這幾個(gè)同學(xué)全都站起來(lái)了(等待在監(jiān)視器鎖上的線程都被喚醒了)。

這樣以來(lái),即使自己被喚醒后,搶到了監(jiān)視器鎖,發(fā)現(xiàn)其實(shí)條件還是不滿足,還是得調(diào)用wait方法掛起,就導(dǎo)致了很多無(wú)意義的時(shí)間和CPU資源的浪費(fèi)。

這一切的根源就在于我們?cè)谡{(diào)用wait方法時(shí)沒(méi)有辦法來(lái)指明究竟是在等待什么樣的條件謂詞上,因此喚醒時(shí),也不知道該喚醒誰(shuí),只能把所有的線程都喚醒了。

因此,最好的方式是,我們?cè)趻炱饡r(shí)就指明了在什么樣的條件謂詞上掛起,同時(shí),在等待的事件發(fā)生后,只喚醒等待在這個(gè)事件上的線程,而實(shí)現(xiàn)了這個(gè)思路的就是Condition接口。

有了Condition接口,我們就可以在同一個(gè)鎖上創(chuàng)建不同的喚醒條件,從而在一定條件謂詞滿足后,有針對(duì)性的喚醒特定的線程,而不是一股腦的將所有等待的線程都喚醒。

Condition的 await/signal 機(jī)制

既然前面說(shuō)了Condition接口的出現(xiàn)是為了拓展現(xiàn)有的wait/notify機(jī)制,那我們就先來(lái)看看現(xiàn)有的wait/notify機(jī)制有哪些方法:

public class Object {
    public final void wait() throws InterruptedException {
        wait(0);
    }
    public final native void wait(long timeout) throws InterruptedException;
    public final void wait(long timeout, int nanos) throws InterruptedException {
        // 這里省略方法的實(shí)現(xiàn)
    }
    public final native void notify();
    public final native void notifyAll();
}

接下來(lái)我們?cè)倏纯碈ondition接口有哪些方法:

public interface Condition {
    void await() throws InterruptedException;
    long awaitNanos(long nanosTimeout) throws InterruptedException;
    boolean await(long time, TimeUnit unit) throws InterruptedException;
    
    void awaitUninterruptibly();
    boolean awaitUntil(Date deadline) throws InterruptedException;
    
    void signal();
    void signalAll();
}

對(duì)比發(fā)現(xiàn),這里存在明顯的對(duì)應(yīng)關(guān)系:

Object 方法 Condition 方法 區(qū)別
void wait() void await()
void wait(long timeout) long awaitNanos(long nanosTimeout) 時(shí)間單位,返回值
void wait(long timeout, int nanos) boolean await(long time, TimeUnit unit) 時(shí)間單位,參數(shù)類型,返回值
void notify() void signal()
void notifyAll() void signalAll()
- void awaitUninterruptibly() Condition獨(dú)有
- boolean awaitUntil(Date deadline) Condition獨(dú)有

它們?cè)诮涌诘囊?guī)范上都是差不多的,只不過(guò)wait/notify機(jī)制針對(duì)的是所有在監(jiān)視器鎖的wait set中的線程,而await/signal機(jī)制針對(duì)的是所有等待在該Condition上的線程。

這里多說(shuō)一句,在接口的規(guī)范中,wait(long timeout)的時(shí)間單位是毫秒(milliseconds), 而awaitNanos(long nanosTimeout)的時(shí)間單位是納秒(nanoseconds), 就這一點(diǎn)而言,awaitNanos這個(gè)方法名其實(shí)語(yǔ)義上更清晰,并且相對(duì)于wait(long timeout, int nanos)這個(gè)略顯雞肋的方法(之前的分析中我們已經(jīng)吐槽過(guò)這個(gè)方法的實(shí)現(xiàn)了),await(long time, TimeUnit unit)這個(gè)方法就顯得更加直觀和有效。

另外一點(diǎn)值得注意的是,awaitNanos(long nanosTimeout)有返回值的,它返回了剩余等待的時(shí)間;await(long time, TimeUnit unit)也是有返回值的,如果該方法是因?yàn)槌瑫r(shí)時(shí)間到了而返回的,則該方法返回false, 否則返回true。

大家有沒(méi)有覺(jué)的奇怪,同樣是帶超時(shí)時(shí)間的等待,為什么wait方式?jīng)]有返回值,await方式有返回值呢。
存在即合理,既然多加了返回值,自然是有它的用意,那么這個(gè)多加的返回值有什么用呢?

我們知道,當(dāng)一個(gè)線程從帶有超時(shí)時(shí)間的wait/await方法返回時(shí),必然是發(fā)生了以下4種情況之一:

其他線程調(diào)用了notify/signal方法,并且當(dāng)前線程恰好是被選中來(lái)喚醒的那一個(gè)

其他線程調(diào)用了notifyAll/signalAll方法

其他線程中斷了當(dāng)前線程

超時(shí)時(shí)間到了

其中,第三條會(huì)拋出InterruptedException,是比較容易分辨的;除去這個(gè),當(dāng)wait方法返回后,我們其實(shí)無(wú)法區(qū)分它是因?yàn)槌瑫r(shí)時(shí)間到了返回了,還是被notify返回的。但是對(duì)于await方法,因?yàn)樗怯蟹祷刂档?,我們就能夠通過(guò)返回值來(lái)區(qū)分:

如果awaitNanos(long nanosTimeout)的返回值大于0,說(shuō)明超時(shí)時(shí)間還沒(méi)到,則該返回是由signal行為導(dǎo)致的

如果await(long time, TimeUnit unit)返回true, 說(shuō)明超時(shí)時(shí)間還沒(méi)到,則該返回是由signal行為導(dǎo)致的

源碼的注釋也說(shuō)了,await(long time, TimeUnit unit)相當(dāng)于調(diào)用awaitNanos(unit.toNanos(time)) > 0

所以,它們的返回值能夠幫助我們弄清楚方法返回的原因。

Condition接口中還有兩個(gè)在Object中找不到對(duì)應(yīng)的方法:

void awaitUninterruptibly();
boolean awaitUntil(Date deadline) throws InterruptedException;

前面說(shuō)的所有的wait/await方法,它們方法的簽名中都拋出了InterruptedException,說(shuō)明他們?cè)诘却倪^(guò)程中都是響應(yīng)中斷的,awaitUninterruptibly方法從名字中就可以看出,它在等待鎖的過(guò)程中是不響應(yīng)中斷的,所以沒(méi)有InterruptedException拋出。也就是說(shuō),它會(huì)一直阻塞,直到signal/signalAll被調(diào)用。如果在這過(guò)程中線程被中斷了,它并不響應(yīng)這個(gè)中斷,只是在該方法返回的時(shí)候,該線程的中斷標(biāo)志位將是true, 調(diào)用者可以檢測(cè)這個(gè)中斷標(biāo)志位以輔助判斷在等待過(guò)程中是否發(fā)生了中斷,以此決定要不要做額外的處理。

boolean awaitUntil(Date deadline)boolean await(long time, TimeUnit unit) 其實(shí)作用是差不多的,返回值代表的含義也一樣,只不過(guò)一個(gè)是相對(duì)時(shí)間,一個(gè)是絕對(duì)時(shí)間,awaitUntil方法的參數(shù)是Date,表示了一個(gè)絕對(duì)的時(shí)間,即截止日期,在這個(gè)日期之前,該方法會(huì)一直等待,除非被signal或者被中斷。

至此,Lock接口和Condition接口我們就分析完了。

我們將在下一篇中給出Lock接口的具體實(shí)現(xiàn)的例子,在逐行分析AQS源碼(4)——Condition接口實(shí)現(xiàn)中給出Condition接口具體實(shí)現(xiàn)的例子。

(完)

系列文章目錄

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/77184.html

相關(guān)文章

  • 系列文章目錄

    摘要:為了避免一篇文章的篇幅過(guò)長(zhǎng),于是一些比較大的主題就都分成幾篇來(lái)講了,這篇文章是筆者所有文章的目錄,將會(huì)持續(xù)更新,以給大家一個(gè)查看系列文章的入口。 前言 大家好,筆者是今年才開(kāi)始寫博客的,寫作的初衷主要是想記錄和分享自己的學(xué)習(xí)經(jīng)歷。因?yàn)閷懽鞯臅r(shí)候發(fā)現(xiàn),為了弄懂一個(gè)知識(shí),不得不先去了解另外一些知識(shí),這樣以來(lái),為了說(shuō)明一個(gè)問(wèn)題,就要把一系列知識(shí)都了解一遍,寫出來(lái)的文章就特別長(zhǎng)。 為了避免一篇...

    lijy91 評(píng)論0 收藏0
  • 系列文章目錄

    摘要:為了避免一篇文章的篇幅過(guò)長(zhǎng),于是一些比較大的主題就都分成幾篇來(lái)講了,這篇文章是筆者所有文章的目錄,將會(huì)持續(xù)更新,以給大家一個(gè)查看系列文章的入口。 前言 大家好,筆者是今年才開(kāi)始寫博客的,寫作的初衷主要是想記錄和分享自己的學(xué)習(xí)經(jīng)歷。因?yàn)閷懽鞯臅r(shí)候發(fā)現(xiàn),為了弄懂一個(gè)知識(shí),不得不先去了解另外一些知識(shí),這樣以來(lái),為了說(shuō)明一個(gè)問(wèn)題,就要把一系列知識(shí)都了解一遍,寫出來(lái)的文章就特別長(zhǎng)。 為了避免一篇...

    Yumenokanata 評(píng)論0 收藏0
  • 逐行分析AQS源碼(4)——Condition接口實(shí)現(xiàn)

    摘要:前言本篇文章是基于線程間的同步與通信和這篇文章寫的,在那篇文章中,我們分析了接口所定義的方法,本篇我們就來(lái)看看對(duì)于接口的這些接口方法的具體實(shí)現(xiàn)。因此,條件隊(duì)列在出隊(duì)時(shí),線程并不持有鎖。 前言 本篇文章是基于線程間的同步與通信(4)——Lock 和 Condtion 這篇文章寫的,在那篇文章中,我們分析了Condition接口所定義的方法,本篇我們就來(lái)看看AQS對(duì)于Condition接口...

    未東興 評(píng)論0 收藏0
  • 線程間的同步通信(7)——CyclicBarrier源碼分析

    摘要:例如,線程需要互相等待,保證所有線程都執(zhí)行完了之后才能一起通過(guò)。獲取正在等待中的線程數(shù)注意,這里加了鎖,因?yàn)榉椒赡軙?huì)被多個(gè)線程同時(shí)修改。只要有一行沒(méi)有處理完,所有的線程都會(huì)在處等待,最后一個(gè)執(zhí)行完的線程將會(huì)負(fù)責(zé)喚醒所有等待的線程 前言 系列文章目錄 上一篇 我們學(xué)習(xí)了基于AQS共享鎖實(shí)現(xiàn)的CountDownLatch,本篇我們來(lái)看看另一個(gè)和它比較像的并發(fā)工具CyclicBarrier...

    freewolf 評(píng)論0 收藏0
  • 線程間的同步通信(3)——淺析synchronized的實(shí)現(xiàn)原理

    摘要:由此可見(jiàn),自旋鎖和各有優(yōu)劣,他們分別適用于競(jìng)爭(zhēng)不多和競(jìng)爭(zhēng)激烈的場(chǎng)景中。每一個(gè)試圖進(jìn)入同步代碼塊的線程都會(huì)被封裝成對(duì)象,它們或在對(duì)象的中,或在中,等待成為對(duì)象的成為的對(duì)象即獲取了監(jiān)視器鎖。 前言 系列文章目錄 前面兩篇文章我們介紹了synchronized同步代碼塊以及wait和notify機(jī)制,大致知道了這些關(guān)鍵字和方法是干什么的,以及怎么用。 但是,知其然,并不知其所以然。 例如...

    keithxiaoy 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

閱讀需要支付1元查看
<