在线观看www成人影院-在线观看www日本免费网站-在线观看www视频-在线观看操-欧美18在线-欧美1级

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

Lock與Condition接口條件變量方式

科技綠洲 ? 來源:Java技術指北 ? 作者:Java技術指北 ? 2023-10-13 11:21 ? 次閱讀

我們知道,并發領域中有兩大核心問題:互斥與同步問題,Java在1.5版本之前,是提供了synchronized來實現的。synchronized是內置鎖,雖然在大部分情況下它都能很好的工作,但是依然還是會存在一些局限性,除了當時1.5版本的性能問題外(1.6版本后,synchronized的性能已經得到了很大的優化),還有如下兩個問題:

  1. 無法解決死鎖問題
  2. 最多使用一個條件變量

所以針對這些問題,Doug Lea在并發包中增加了兩個接口Lock和Condition來解決這兩個問題,所以今天就說說這兩個接口是如何解決synchronized中的這兩個問題的。

一. Lock接口

1.1 介紹

在我們分析Lock接口是如何解決死鎖問題之前,我們先看看死鎖是如何產生的。死鎖的產生需要滿足下面四個條件:

  1. 互斥 :共享資源同一時間只能被一個線程占用
  2. 不可搶占 :其他線程不能強行占有另一個線程的資源
  3. 占有且等待 :線程在等待其他資源時,不釋放自己已占有的資源
  4. 循環等待 :線程1和線程2互相占有對方的資源并相互等待

所以,我們只需要破壞上面條件中的任意一個,即可打破死鎖。但需要注意的是,互斥條件是不能破壞的,因為使用鎖的目的就是為了互斥。所以Lock接口通過破壞掉 "不可搶占"這個條件來解決死鎖,具體如下:

  1. 非阻塞獲取鎖 :嘗試獲取鎖,如果失敗了就立刻返回失敗,這樣就可以釋放已經持有的其他鎖
  2. 響應中斷 :如果發生死鎖后,此線程被其他線程中斷,則會釋放鎖,解除死鎖
  3. 支持超時 :一段時間內獲取不到鎖,就返回失敗,這樣就可以釋放之前已經持有的鎖

接下來我們具體看看接口代碼吧。

1.2 源碼解讀

public interface Lock {
    /**
        阻塞獲取鎖,不響應中斷,如果獲取不到,則當前線程將進入休眠狀態,直到獲得鎖為止。
    */
    void lock();

    /**
        阻塞獲取鎖,響應中斷,如果出現以下兩種情況將拋出異常
        1.調用該方法時,此線程中斷標志位被設置為true
        2.獲取鎖的過程中此線程被中斷,并且獲取鎖的實現會響應中斷
    */
    void lockInterruptibly() throws InterruptedException;
  
    /**
        非阻塞獲取鎖,不管成功還是失敗,都會立刻返回結果,成功了返回true,失敗了返回false
     */
    boolean tryLock();
 
    /**
      帶超時時間且響應中斷的獲取鎖,如果獲取鎖成功,則返回true,獲取不到則會休眠,直到下面三個條件滿足
      1.當前線程獲取到鎖
      2.其他線程中斷了當前線程,并且獲取鎖的實現支持中斷
      3.設置的超時事件到了
      而拋出異常的情況與lockInterruptibly一致
      當異常拋出后中斷標志位會被清除,且超時時間到了,當前線程還沒有獲得鎖,則會直接返回false
     */
    boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
   
    /**
        沒啥好說,只有擁有鎖的線程才能釋放鎖
     */
    void unlock();

    /**
        返回綁定到此Lock實例的新Condition實例。
        在等待該條件之前,該鎖必須由當前線程持有。調用Condition.await()會在等待之前自動釋放鎖,并在等待返回之前重新獲取該鎖。
        我們再下一小節再詳細說說Condition接口
     */
    Condition newCondition();
}

還需要額外注意的一點,使用synchronized作為鎖時,我們是不需要考慮釋放鎖的,但Lock是屬于顯示鎖,是需要我們手動釋放鎖的。我們一般在finally塊中調用lock.unlock()手動釋放鎖,具體形式如下:

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

我們最后通過一張圖來總結下Lock接口:

圖片

二. Condition接口

2.1 介紹

針對synchronized最多只能使用一個條件變量的問題,Condition接口提供了解決方案。但是為什么多個條件變量就比一個條件變量好呢?我們先來看看synchronized使用一個條件變量時會有什么弊端。

一個synchronized內置鎖只對應一個等待容器(wait set),當線程調用wait方法時,會把當前線程放入到同一個等待容器中,當我們需要根據某些特定的條件來喚醒符合條件的線程時,我們只能先從等待容器里喚醒一個線程后,再看是否符合條件。如果不符合條件,則需要將此線程繼續wait,然后再去等待容器中獲取下一個線程再判斷是否滿足條件。這樣會導致許多無意義的cpu開銷。

我們可以看到Lock接口中有個newCondition()的方法:

Condition newCondition();

通過這個方法,一個鎖可以建立多個Conditiion,每個Condtition都有一個容器來保存相應的等待線程,拿到鎖的線程根據特定的條件喚醒對應的線程時,只需要去喚醒對應的Contition內置容器中的線程即可,這樣就可以減少無意義的CPU開銷。然后我們具體看看Condition接口的源碼。

2.2 源碼解讀

public interface Condition {

    /**
 使當前線程等待,并響應中斷。當當前線程進入休眠狀態后,如果發生以下四種情況將會被喚醒:
 1.其他一些線程對此條件調用signal方法,而當前線程恰好被選擇為要喚醒的線程;
 2.其他一些線程對此條件調用signalAll方法
 3.其他一些線程中斷當前線程,并支持中斷線程掛起
 4.發生“虛假喚醒”。
     */
    void await() throws InterruptedException;

    /**
 使當前線程等待,并不響應中斷。只有以下三種情況才會被喚醒
 1.其他一些線程對此條件調用signal方法,而當前線程恰好被選擇為要喚醒的線程;
 2.其他一些線程對此條件調用signalAll方法
 3.發生“虛假喚醒”。
     */
    void awaitUninterruptibly();

    /**
        使當前線程等待,響應中斷,且可以指定超時事件。發生以下五種情況之一將會被喚醒:
        1.其他一些線程為此條件調用signal方法,而當前線程恰好被選擇為要喚醒的線程;
        2.其他一些線程為此條件調用signalAll方法;
        3.其他一些線程中斷當前線程,并且支持中斷線程掛起;
        4.經過指定的等待時間;
        5.發生“虛假喚醒”。
     */
    long awaitNanos(long nanosTimeout) throws InterruptedException;

    /**
 與awaitNanos類似,時間單位不同
     */
    boolean await(long time, TimeUnit unit) throws InterruptedException;

    /**
 與awaitNanos類似,只不過超時時間是截止時間
     */
    boolean awaitUntil(Date deadline) throws InterruptedException;

    /**
 喚醒一個等待線程
     */
    void signal();

    /**
 喚醒所有等待線程
     */
    void signalAll();
}

需要注意的是,Object類的等待方法是沒有返回值的,但Condtition類中的部分等待方法是有返回值的。awaitNanos(long nanosTimeout)返回了剩余等待的時間;await(long time, TimeUnit unit)返回boolean值,如果返回false,則說明是因為超時返回的,否則返回true。為什么增加返回值?為了就是幫助我們弄清楚方法返回的原因。

四. 阿里多線程考題

最后我們通過實現了Lock和Condition接口能力的ReentrantLock類來解決阿里多線程面試題。

題目是使用三個線程循環打印ABC,一共打印50次。我們直接上答案:

public class Test {


    int count = 0;
    Lock lock = new ReentrantLock();
    Condition conditionA = lock.newCondition();
    Condition conditionB = lock.newCondition();
    Condition conditionC = lock.newCondition();

    public void printA() {
        while (count < 50) {
            try {
                // 加鎖
                lock.lock();
                // 打印A
                System.out.println("A");
                count ++;
                // 喚醒打印B的線程
                conditionB.signal();
                // 將自己放入ConditionA的容器中,等待其他線程的喚醒
                conditionA.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                // 釋放鎖
                lock.unlock();
            }
        }


    }

    public void printB() {
        while (count < 50) {
            try {
                // 加鎖
                lock.lock();
                // 打印B
                System.out.println("B");
                count ++;
                // 喚醒打印C的線程
                conditionC.signal();
                // 將自己放入ConditionB的容器中,等待其他線程的喚醒
                conditionB.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                // 釋放鎖
                lock.unlock();
            }
        }
    }


    public void printC() {
        while (count < 50) {
            try {
                // 加鎖
                lock.lock();
                // 打印B
                System.out.println("C");
                count ++;
                // 喚醒打印A的線程
                conditionA.signal();
                // 將自己放入ConditionC的容器中,等待其他線程的喚醒
                conditionC.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
    }

    public static void main(String[] args) {
        Test test = new Test();
        // 建立打印ABC的三個線程
        Thread theadA = new Thread(() - > {
            test.printA();
        });
        Thread theadB = new Thread(() - > {
            test.printB();
        });
        Thread theadC = new Thread(() - > {
            test.printC();
        });
    
        // 啟動線程
        theadA.start();
        theadB.start();
        theadC.start();

    }
}

五. 總結

Lock與Condition接口就說完了,最后再總結一下:

針對synchronized內置鎖無法解決死鎖、只有一個條件變量等問題,Doug Lea在Java并發包中增加了Lock和Condition接口來解決。對于死鎖問題,Lock接口增加了超時、響應中斷、非阻塞三種方式來獲取鎖,從而避免了死鎖。針對一個條件變量問題,Condtition接口通過一把鎖可以創建多個條件變量的方式來解決。

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • 接口
    +關注

    關注

    33

    文章

    8634

    瀏覽量

    151370
  • JAVA
    +關注

    關注

    19

    文章

    2971

    瀏覽量

    104848
  • Lock
    +關注

    關注

    0

    文章

    10

    瀏覽量

    7776
  • 線程
    +關注

    關注

    0

    文章

    505

    瀏覽量

    19705
收藏 人收藏

    評論

    相關推薦

    Linux下線程間通訊---讀寫鎖和條件變量

    讀寫鎖,它把對共享資源的訪問者劃分成讀者和寫者,讀者只對共享資源進行讀訪問,寫者則需要對共享資源進行寫操作。件變量是線程可用的一種同步機制,條件變量給多個線程提供了一個回合的場所,條件
    的頭像 發表于 08-26 20:44 ?1506次閱讀
    Linux下線程間通訊---讀寫鎖和<b class='flag-5'>條件</b><b class='flag-5'>變量</b>

    Linux系統中線程同步方式中的條件變量方法

    今天主要和大家聊一聊,如何使用Linux中線程同步方式中的條件變量
    發表于 11-08 09:16 ?541次閱讀

    labview枚舉變量條件結構的結合的問題

    如圖:程序運行時,為什么在前面板上改變枚舉變量的值,條件結構的條件中對應的沒有改變呢?
    發表于 12-06 15:21

    Raw condition msg 篇

    調用這個函數會立即block 在condition msg上,直到其他任務調用raw_cond_msg_set,滿足條件后才會醒過來并接收到一個消息。Wait_option 可以設置為
    發表于 02-27 14:08

    條件結構的布爾變量問題?

    本帖最后由 dsl7410 于 2016-1-13 00:00 編輯 這個順序結構里面的條件結構一個是把value值+1,一個是-1,+1可以理解,因為分支選擇器接了加的布爾變量,但是減的那個分支怎么實現呢?分支選擇器沒有連接減啊,那個減的布爾控件刪除了,還能實現-
    發表于 01-12 23:57

    Linux C 多線程編程之互斥鎖與條件變量實例詳解

    。這時線程掛起,不占用 CPU 時間,直到條件變量被觸發。因此,全過程可以描述為:(1)pthread_mutex_lock()上鎖,(2)pthread_cond_wait()等待,等待過程分解為為
    發表于 06-03 17:13

    淺析linux下的條件變量

    變量中常用的API: ? ? ?1).條件變量類型為:pthread_cond_t ,類似互斥變量條件
    發表于 07-12 08:10

    Lock體系結構和讀寫鎖機制解析

    排它性,即同一個時刻只有一個線程進入任務。Condition接口Condition接口描述可能會與鎖有關聯的條件
    發表于 01-05 17:53

    【隨筆記】C++ condition_variable 陷阱

    ; lock(mutex_data_); cond_.notify_all(); } 改進方案一(使用 select 方式實現):缺點是一個對象會浪費兩個文件描述符資源 DelayControl
    發表于 11-24 10:41

    FIDIC合同條件體系及應用方式

    介紹了FIDIC組織、FIDIC合同條件體系及其最新發展,討論了FIDIC合同條件的應用方式,強調了學習FIDIC合同條件的意義和重要性。
    發表于 01-08 15:32 ?2次下載

    linux設置環境變量的三種方式

     linux設置環境變量有以下三種方式
    發表于 06-15 09:05 ?1431次閱讀
    linux設置環境<b class='flag-5'>變量</b>的三種<b class='flag-5'>方式</b>

    詳談Linux操作系統編程的條件變量

    條件變量是用來等待線程而不是上鎖的,條件變量通常和互斥鎖一起使用。條件變量之所以要和互斥鎖一起使
    的頭像 發表于 09-27 15:23 ?2012次閱讀
    詳談Linux操作系統編程的<b class='flag-5'>條件</b><b class='flag-5'>變量</b>

    TensorRT條件用于實現網絡子圖的條件執行

    IIfConditional實現了一個 if-then-else 流控制結構,該結構提供基于動態布爾輸入的網絡子圖的條件執行。它由一個布爾標量predicate condition和兩個分支子圖定義
    的頭像 發表于 05-18 10:02 ?1186次閱讀

    Linux線程條件變量是什么意思

    條件變量 條件變量用于自動阻塞線程,直到某個特定事件發生或某個條件滿足為止,通常情況下,條件
    的頭像 發表于 07-21 11:18 ?530次閱讀

    case怎么使用多個條件

    在編寫代碼時,我們經常需要根據不同的條件來執行不同的操作。在Python中,我們可以使用 if 語句來實現這一目的。 if 語句允許我們設置多個條件,并且根據不同的條件執行不同的代碼塊。 語法結構
    的頭像 發表于 11-30 14:34 ?1235次閱讀
    主站蜘蛛池模板: 中国美女一级黄色片| 97国产影院| 欧色视频| 免费能看的黄色网址| 精品噜噜噜噜久久久久久久久| 久久久五月| 国产午夜大片| 91网视频在线观看| 天天射天天操天天| 狠狠艹视频| www.啪啪.com| 伊人亚洲| 色屋网| 毛片毛多| brazzersvideosex欧美最| 午夜三级福利| 国产精品资源在线观看网站| 欧美乱xxxxxxxxx| 韩漫免费网站无遮挡羞羞漫画| 亚洲婷婷在线视频| 日本亚洲精品色婷婷在线影院| 看全色黄大色大片免费久久 | 五月激情啪啪网| 亚洲一区二区三区网站| 在线观看亚洲人成网站| 色咪网| 国产美女特级嫩嫩嫩bbb| 天天操 夜夜操| 啪啪.com| 欲色影视香色天天影视来| 日韩啪啪电影| 国产成人三级视频在线观看播放| 天天射夜夜操| 欧美三级在线| 一级毛片成人免费看a| 欧美三级免费看| 中日韩一级片| 2018国产精品| 一色屋网站| 欧美zoozzooz性欧美| 在线亚洲小视频|