欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java多線程wait()和notify()方法詳細圖解

 更新時間:2022年10月31日 15:04:16   作者:Killing?Vibe  
wait()和notify()是直接隸屬于Object類,也就是說所有對象都擁有這一對方法,下面這篇文章主要給大家介紹了關于Java多線程wait()和notify()方法詳細圖解的相關資料,需要的朋友可以參考下

一、線程間等待與喚醒機制

wait()和notify()是Object類的方法,用于線程的等待與喚醒,必須搭配synchronized 鎖來使用。

多線程并發(fā)的場景下,有時需要某些線程先執(zhí)行,這些線程執(zhí)行結束后其他線程再繼續(xù)執(zhí)行。

比如: 一個長跑比賽,裁判員要等跑步運動員沖線了才能宣判比賽結束,那裁判員線程就得等待所有的運動員線程運行結束后,再喚醒這個裁判線程。

二、等待方法wait()

wait 做的事情:

  • 使當前執(zhí)行代碼的線程進行等待. (把線程放到等待隊列中)
  • 釋放當前的鎖
  • 滿足一定條件時被喚醒, 重新嘗試獲取這個鎖.

wait 要搭配 synchronized 來使用. 脫離 synchronized 使用 wait 會直接拋出異常.

wait 結束等待的條件:

  • 其他線程調(diào)用該對象的 notify 方法.
  • wait 等待時間超時 (wait 方法提供一個帶有 timeout 參數(shù)的版本, 來指定等待時間).
  • 其他線程調(diào)用該等待線程的 interrupted 方法, 導致 wait 拋出 InterruptedException 異常.

注意事項:

  1. 調(diào)用wait()方法的前提是首先要獲取該對象的鎖(synchronize對象鎖)
  2. 調(diào)用wait()方法會釋放鎖,本線程進入等待隊列等待被喚醒,被喚醒后不是立即恢復執(zhí)行,而是進入阻塞隊列,競爭鎖

等待方法:

1.癡漢方法,死等,線程進入阻塞態(tài)(WAITING)直到有其他線程調(diào)用notify方法喚醒

2.等待一段時間,若在該時間內(nèi)線程被喚醒,則繼續(xù)執(zhí)行,若超過相應時間還沒有其他線程喚醒此線程,此線程不再等待,恢復執(zhí)行。

調(diào)用wait方法之后:

三、喚醒方法notify()

notify 方法是喚醒等待的線程.

  • 方法notify()也要在同步方法或同步塊中調(diào)用,該方法是用來通知那些可能等待該對象的對象鎖的其它線程,對其發(fā)出通知notify,并使它們重新獲取該對象的對象鎖。
  • 如果有多個線程等待,則有線程調(diào)度器隨機挑選出一個呈 wait 狀態(tài)的線程。(并沒有 “先來后到”)
  • 在notify()方法后,當前線程不會馬上釋放該對象鎖,要等到執(zhí)行notify()方法的線程將程序執(zhí)行完,也就是退出同步代碼塊之后才會釋放對象鎖。

注意事項:

  • notify():隨機喚醒一個處在等待狀態(tài)的線程。
  • notifyAll():喚醒所有處在等待狀態(tài)的線程。
  • 無論是wait還是notify方法,都需要搭配synchronized鎖來使用(等待和喚醒,也是需要對象)

四、關于wait和notify內(nèi)部等待問題(重要)

對于wait和notify方法,其實有一個阻塞隊列也有一個等待隊列。

  • 阻塞隊列表示同一時間只有一個線程能獲取到鎖,其他線程進入阻塞隊列
  • 等待隊列表示調(diào)用wait (首先此線程要獲取到鎖,進入等待隊列,釋放鎖

舉個栗子:

現(xiàn)有如下定義的等待線程任務

private static class WaitTask implements Runnable {
        private Object lock;
        public WaitTask(Object lock) {
            this.lock = lock;
        }
        @Override
        public void run() {
            synchronized (lock) {
                System.out.println(Thread.currentThread().getName() + "準備進入等待狀態(tài)");
                // 此線程在等待lock對象的notify方法喚醒
                try {
                    lock.wait();
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                System.out.println(Thread.currentThread().getName() + "等待結束,本線程繼續(xù)執(zhí)行");
            }
        }
    }

然后創(chuàng)建三個等待線程:

由于同一時間只有一個線程(隨機調(diào)度)能獲取到synchronized鎖,所以會有兩個線程沒競爭到鎖,從而進入了阻塞隊列。

這里假如t2先競爭到了鎖,所以先會阻塞t1和t3:

又由于調(diào)用wait方法會釋放鎖,調(diào)用wait方法的線程t2就會進入等待隊列,直到被notify喚醒或者超時自動喚醒。

然后此時lock對象已經(jīng)被釋放了,所以t1和t3 又可以去競爭這個鎖了,就從阻塞隊列里面競爭鎖。

這里假如t3 競爭到了鎖,阻塞隊列只剩下t1:

然后t3運行到了wait方法,釋放鎖,然后進入等待隊列

然后重復這些操作~~,最后t1,t2,t3 都進入了等待隊列中,等待notify線程喚醒(這里假設notify要放在這些線程start后的好幾秒后,因為notify線程也是和這些線程并發(fā)執(zhí)行的,所以等待隊列中的線程隨時可能被喚醒

重點來了:

在等待隊列中的線程,被notify喚醒之后,會直接回到阻塞隊列去競爭鎖?。?!而不是直接喚醒~

舉個栗子:

拿notifyAll()來舉例,假如此時等待隊列中有三個線程t1,t2,t3,那么調(diào)用notifyAll()會直接把它們?nèi)齻€直接從等待隊列中進入到阻塞隊列中:

然后再去競爭這個鎖,去執(zhí)行wait之后的代碼~~

五、完整代碼(僅供測試用)

private static class WaitTask implements Runnable {
        private Object lock;
        public WaitTask(Object lock) {
            this.lock = lock;
        }
        @Override
        public void run() {
            synchronized (lock) {
                System.out.println(Thread.currentThread().getName() + "準備進入等待狀態(tài)");
                // 此線程在等待lock對象的notify方法喚醒
                try {
                    lock.wait();
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                System.out.println(Thread.currentThread().getName() + "等待結束,本線程繼續(xù)執(zhí)行");
            }
        }
    }
    private static class NotifyTask implements Runnable {
        private Object lock;
        public NotifyTask(Object lock) {
            this.lock = lock;
        }
        @Override
        public void run() {
            synchronized (lock) {
                System.out.println("準備喚醒");
                // 喚醒所有線程(隨機)
                lock.notifyAll();
                System.out.println("喚醒結束");
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        Object lock = new Object();
        Object lock2 = new Object();
        // 創(chuàng)建三個等待線程
        Thread t1 = new Thread(new WaitTask(lock),"t1");
        Thread t2 = new Thread(new WaitTask(lock),"t2");
        Thread t3 = new Thread(new WaitTask(lock),"t3");
       // 創(chuàng)建一個喚醒線程
        Thread notify = new Thread(new NotifyTask(lock2),"notify線程");
        t1.start();
        t2.start();
        t3.start();
        ;
        Thread.sleep(100);
        notify.start();
        // 當前正在執(zhí)行的線程數(shù)
        Thread.sleep(2000);
        System.out.println(Thread.activeCount() - 1);
    }

六、wait和sleep方法的區(qū)別(面試題):

  • wait方法是Object類提供的方法,需要搭配synchronized鎖來使用,調(diào)用wait方法會釋放鎖,線程進入WAITING狀態(tài),等待被其他線程喚醒或者超時自動喚醒,喚醒之后的線程需要再次競爭synchronized鎖才能繼續(xù)執(zhí)行。
  • sleep方法是Thread類提供的方法,調(diào)用sleep方法的線程進入TIMED_WAITING狀態(tài),不會釋放鎖,時間到自動喚醒。

總結

以上就是多線程場景下wait和notify方法的詳解和注意事項了,更多相關多線程wait()和notify()方法內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • 詳解java整合solr5.0之solrj的使用

    詳解java整合solr5.0之solrj的使用

    本篇文章主要介紹了詳解java整合solr5.0之solrj的使用 ,具有一定的參考價值,有興趣的可以了解下
    2017-06-06
  • Springboot整合nacos報錯無法連接nacos的解決

    Springboot整合nacos報錯無法連接nacos的解決

    這篇文章主要介紹了Springboot整合nacos報錯無法連接nacos的解決方案,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-06-06
  • Java靜態(tài)方法不具有多態(tài)性詳解

    Java靜態(tài)方法不具有多態(tài)性詳解

    下面小編就為大家?guī)硪黄狫ava靜態(tài)方法不具有多態(tài)性詳解。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2016-06-06
  • Java高并發(fā)中的交換器Exchanger解析

    Java高并發(fā)中的交換器Exchanger解析

    這篇文章主要介紹了Java高并發(fā)中的交換器Exchanger解析,如果兩個線程并行處理,但在某個時刻需要互相交換自己已經(jīng)處理完的中間數(shù)據(jù),然后才能繼續(xù)往下執(zhí)行,這個時候就可以使用 Exchanger,需要的朋友可以參考下
    2023-12-12
  • JavaEE開發(fā)基于Eclipse的環(huán)境搭建以及Maven Web App的創(chuàng)建

    JavaEE開發(fā)基于Eclipse的環(huán)境搭建以及Maven Web App的創(chuàng)建

    本文主要介紹了如何在Eclipse中創(chuàng)建的Maven Project,本文是JavaEE開發(fā)的開篇,也是基礎。下面內(nèi)容主要包括了JDK1.8的安裝、JavaEE版本的Eclipse的安裝、Maven的安裝、Tomcat 9.0的配置、Eclipse上的M2Eclipse插件以及STS插件的安裝。
    2017-03-03
  • Java實現(xiàn)冒泡排序算法及對其的簡單優(yōu)化示例

    Java實現(xiàn)冒泡排序算法及對其的簡單優(yōu)化示例

    這篇文章主要介紹了Java實現(xiàn)冒泡排序算法及對其的簡單優(yōu)化示例,冒泡排序的最差時間復雜度為O(n^2),最優(yōu)時間復雜度為O(n),存在優(yōu)化的余地,需要的朋友可以參考下
    2016-05-05
  • Java 從網(wǎng)上下載文件的幾種方式實例代碼詳解

    Java 從網(wǎng)上下載文件的幾種方式實例代碼詳解

    本文通過實例代碼給大家介紹了java從網(wǎng)上下載文件的幾種方式,非常不錯,具有參考借鑒價值,需要的的朋友參考下吧
    2017-08-08
  • mybatis中的count()按條件查詢方式

    mybatis中的count()按條件查詢方式

    這篇文章主要介紹了mybatis中的count()按條件查詢方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-01-01
  • SpringBoot?對接飛書多維表格事件回調(diào)監(jiān)聽流程分析

    SpringBoot?對接飛書多維表格事件回調(diào)監(jiān)聽流程分析

    本文介紹了如何通過飛書事件訂閱機制和SpringBoot項目集成,對多維表數(shù)據(jù)的記錄變更進行對接的詳細流程,包括如何創(chuàng)建應用、配置參數(shù)、編寫訂閱代碼、訂閱文檔事件以及在SpringBoot工程中集成的步驟,感興趣的朋友跟隨小編一起看看吧
    2024-12-12
  • SpringBoot加密配置文件的SQL賬號密碼方式

    SpringBoot加密配置文件的SQL賬號密碼方式

    這篇文章主要介紹了SpringBoot加密配置文件的SQL賬號密碼方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-06-06

最新評論