成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

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

Aceyclee / 2989人閱讀

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

前言

系列文章目錄

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

與內(nèi)置鎖不同,Lock是一種顯式鎖,它更加“危險”,因為在程序離開被鎖保護的代碼塊時,不會像監(jiān)視器鎖那樣自動釋放,需要我們手動釋放鎖。所以,在我們使用lock鎖時,一定要記得:
在finally塊中調(diào)用lock.unlock()手動釋放鎖!?。?/em>
在finally塊中調(diào)用lock.unlock()手動釋放鎖?。。?/em>
在finally塊中調(diào)用lock.unlock()手動釋放鎖?。?!

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接口定義了四種獲取鎖的方式,下面我們一個個來看

lock()

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

lockInterruptibly()

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

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

在獲取鎖的過程中,線程被中斷了,并且鎖的獲取實現(xiàn)會響應(yīng)這個中斷

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

tryLock()

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

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

tryLock(long time, TimeUnit unit)

帶超時機制,并且可中斷

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

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

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

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

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

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

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

在獲取鎖的過程中,線程被中斷了,并且鎖的獲取實現(xiàn)會響應(yīng)這個中斷

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

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

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

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

鎖的釋放

相對于鎖的獲取,鎖的釋放的方法就簡單的多,只有一個

void unlock();

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

newCondition

Lock接口還定義了一個newCondition方法:

Condition newCondition();

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

Condition 接口

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

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

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

通常情況下,我們調(diào)用wait方法,主要是因為一定的條件沒有滿足,我們把需要滿足的事件或條件稱作條件謂詞。

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

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

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

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

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

Condition的 await/signal 機制

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

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 {
        // 這里省略方法的實現(xiàn)
    }
    public final native void notify();
    public final native void notifyAll();
}

接下來我們再看看Condition接口有哪些方法:

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();
}

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

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

它們在接口的規(guī)范上都是差不多的,只不過wait/notify機制針對的是所有在監(jiān)視器鎖的wait set中的線程,而await/signal機制針對的是所有等待在該Condition上的線程。

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

另外一點值得注意的是,awaitNanos(long nanosTimeout)有返回值的,它返回了剩余等待的時間;await(long time, TimeUnit unit)也是有返回值的,如果該方法是因為超時時間到了而返回的,則該方法返回false, 否則返回true。

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

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

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

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

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

超時時間到了

其中,第三條會拋出InterruptedException,是比較容易分辨的;除去這個,當(dāng)wait方法返回后,我們其實無法區(qū)分它是因為超時時間到了返回了,還是被notify返回的。但是對于await方法,因為它是有返回值的,我們就能夠通過返回值來區(qū)分:

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

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

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

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

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

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

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

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

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

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

(完)

系列文章目錄

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

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/77184.html

相關(guān)文章

  • 系列文章目錄

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

    lijy91 評論0 收藏0
  • 系列文章目錄

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

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

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

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

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

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

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

    keithxiaoy 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<