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

Java多線程中wait?notify等待喚醒機制詳解

 更新時間:2024年10月03日 10:35:06   作者:IYF.星辰  
這篇文章主要介紹了Java多線程中wait?notify等待喚醒機制,由于線程之間是搶占式執(zhí)行的,因此線程的執(zhí)行順序難以預(yù)知,但是實際開發(fā)中有時候我們希望合理的協(xié)調(diào)多個線程之間的執(zhí)行先后順序,所以這里我們來介紹下等待喚醒機制,需要的朋友可以參考下

一.等待喚醒機制簡介

由于線程之間是搶占式執(zhí)行的,因此線程的執(zhí)行順序難以預(yù)知。但是實際開發(fā)中有時候我們希望合理的協(xié)調(diào)多個線程之間的執(zhí)行先后順序。為了完成協(xié)調(diào)的工作,這里主要設(shè)計三個方法:

  • wait() / wait(long timeout) : 讓當(dāng)前線程進入等待狀態(tài)
  • notify() / notifyAll(): 喚醒在當(dāng)前對象上等待的線程

注意:wait,notify,notifyAll都是Object類的方法

二.synchronized與wait()與notify()

  • synchronized的含義:

Java中每一個對象都可以成為一個監(jiān)視器(Monitor), 該Monitor由一個鎖(lock), 一個等待隊列(waiting queue ), 一個入口隊列( entry queue).

對于一個對象的方法, 如果沒有synchronized關(guān)鍵字, 該方法可以被任意數(shù)量的線程,在任意時刻調(diào)用。

對于添加了synchronized關(guān)鍵字的方法,任意時刻只能被唯一的一個獲得了對象實例鎖的線程調(diào)用。

synchronized用于實現(xiàn)多線程的同步操作

  • wait()功能:

wait(), notify(), notifyAll() 和 synchonized 需要搭配使用, 用于線程同步

wait()總是在一個循;環(huán)中被調(diào)用,掛起當(dāng)前線程來等待一個條件的成立。 Wait調(diào)用會一直等到其他線程調(diào)用notifyAll()時才返回。

當(dāng)一個線程在執(zhí)行synchronized 的方法內(nèi)部,調(diào)用了wait()后, 該線程會釋放該對象的鎖, 然后該線程會被添加到該對象的等待隊列中(waiting queue), 只要該線程在等待隊列中, 就會一直處于閑置狀態(tài), 不會被調(diào)度執(zhí)行。 要注意wait()方法會強迫線程先進行釋放鎖操作,所以在調(diào)用wait()時, 該線程必須已經(jīng)獲得鎖,否則會拋出異常。由于wait()在synchonized的方法內(nèi)部被執(zhí)行, 鎖一定已經(jīng)獲得, 就不會拋出異常了。

  • notify()的功能:

wait(), notify(), notifyAll() 和 synchonized 需要搭配使用, 用于線程同步

當(dāng)一個線程調(diào)用一個對象的notify()方法時, 調(diào)度器會從所有處于該對象等待隊列(waiting queue)的線程中取出任意一個線程, 將其添加到入口隊列( entry queue) 中. 然后在入口隊列中的多個線程就會競爭對象的鎖, 得到鎖的線程就可以繼續(xù)執(zhí)行。 如果等待隊列中(waiting queue)沒有線程, notify()方法不會產(chǎn)生任何作用

notifyAll() 和notify()工作機制一樣, 區(qū)別在于notifyAll()會將等待隊列(waiting queue)中所有的線程都添加到入口隊列中(entry queue)

三.等待喚醒機制案例

1.讓t1執(zhí)行wait()方法。

2.此時t2得到鎖,再讓t2執(zhí)行notify()方法釋放鎖。

3.此時t1得到鎖,t1會自動從wait()方法之后的代碼,繼續(xù)執(zhí)行。

4.通過上述流程,我們就可以清楚的看到,wait()和notify()各自是怎么工作的了,也可以知道兩者是怎么配合的了。

import java.util.*;
public class Main {
    //創(chuàng)建一個將被兩個線程同時訪問的共享對象
    public static Object loker = new Object();
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(()->{
            synchronized (loker){
                System.out.println("線程一初次獲得對象鎖,執(zhí)行過程中調(diào)用鎖對象的wait()方法~~");
                try {
                    loker.wait();
                    System.out.println("當(dāng)線程一被喚醒后,后面的代碼繼續(xù)執(zhí)行");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("線程一運行結(jié)束");
        },"線程一");
        Thread t2 = new Thread(()->{
            synchronized (loker){
                System.out.println("線程二初次獲得對象鎖,執(zhí)行過程中調(diào)用鎖對象的notify()方法~~");
                loker.notify();
                System.out.println("喚醒線程一前,后面的代碼繼續(xù)執(zhí)行~~");
            }
            System.out.println("線程二結(jié)束");
        },"線程二");
        t1.start();
        //防止t2優(yōu)先獲得CPU執(zhí)行權(quán)而錯過喚醒t1
        Thread.sleep(1000);
        t2.start();
    }
}

運行結(jié)果(運行流程也就是運行的打印結(jié)果):

例題一

有三個線程,線程名稱分別為:a,b,c。每個線程打印自己的名稱。

需要讓他們同時啟動,并按 c,b,a的順序打印

代碼詳解:

import java.util.*;
public class Test {
    public static Object loker1 = new Object();
    public static Object loker2 = new Object();
    public static void main(String[] args) {
        System.out.println("打印順序如下:");
        Thread t1 = new Thread(()->{
            //為了防止線程A的喚醒沒有被線程B接受,這里先讓線程A睡一會
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+": C");
            synchronized (loker1){
                loker1.notify();
            }
        },"線程C");
        Thread t2 = new Thread(()->{
            synchronized (loker1){
                //等待線程A的喚醒
                try {
                    loker1.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println(Thread.currentThread().getName()+": B");
            //線程B喚醒線程C
            synchronized (loker2){
                loker2.notify();
            }
        },"線程B");
        Thread t3 = new Thread(()->{
            //線程C等待線程A的喚醒
            synchronized (loker2){
                try {
                    loker2.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println(Thread.currentThread().getName()+": A");
        },"線程A");
        //開啟線程
        t1.start();
        t2.start();
        t3.start();
    }
}

運行結(jié)果:

例題二

有三個線程,分別只能打印A,B和C

要求按順序打印ABC,打印10次

輸出示例:

ABC

ABC

ABC

ABC

ABC

ABC

ABC

ABC

ABC

ABC

大致過程:

代碼詳解:

import java.util.*;
public class Demo {
    private static Object locker1 = new Object();
    private static Object locker2 = new Object();
    private static Object locker3 = new Object();
    public static void main(String[] args) throws InterruptedException {
        System.out.println("打印結(jié)果:");
        Thread t1 = new Thread(() -> {
            try {
                for (int i = 0; i < 10; i++) {
                    synchronized (locker1) {
                        locker1.wait();
                    }
                    System.out.print("A");
                    synchronized (locker2) {
                        locker2.notify();
                    }
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        Thread t2 = new Thread(() -> {
            try {
                for (int i = 0; i < 10; i++) {
                    synchronized (locker2) {
                        locker2.wait();
                    }
                    System.out.print("B");
                    synchronized (locker3) {
                        locker3.notify();
                    }
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        Thread t3 = new Thread(() -> {
            try {
                for (int i = 0; i < 10; i++) {
                    synchronized (locker3) {
                        locker3.wait();
                    }
                    System.out.println("C");
                    synchronized (locker1) {
                        locker1.notify();
                    }
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        t1.start();
        t2.start();
        t3.start();
        //讓三個線程都拿到鎖
        Thread.sleep(1000);
        // 從線程 t1 啟動
        synchronized (locker1) {
            locker1.notify();
        }
    }
}

運行結(jié)果:

四.什么時候釋放鎖——wait()與notify()

由于等待一個鎖定線程只有在獲得這把鎖之后,才能恢復(fù)運行,所以讓持有鎖的線程在不需要鎖的時候及時釋放鎖是很重要的。在以下情況下,持有鎖的線程會釋放鎖:

1.執(zhí)行完同步代碼塊。

2.在執(zhí)行同步代碼塊的過程中,遇到異常而導(dǎo)致線程終止。

3.在執(zhí)行同步代碼塊的過程中,執(zhí)行了鎖所屬對象的wait()方法,這個線程會釋放鎖,進行對象的等待池。

除了以上情況外,只要持有鎖的對象還沒有執(zhí)行完同步代碼塊,就不會釋放鎖。因此在以下情況下,線程不會釋放鎖:

1.在執(zhí)行同步代碼塊的過程中,執(zhí)行了Thread.sleep()方法,當(dāng)前線程放棄CPU,開始睡眠,在睡眠中不會釋放鎖。

2.在執(zhí)行同步代碼塊的過程中,執(zhí)行了Thread.yield()方法,當(dāng)前線程放棄CPU,但不會釋放鎖。

3.在執(zhí)行同步代碼塊的過程中,其他線程執(zhí)行了當(dāng)前對象的suspend()方法,當(dāng)前線程被暫停,但不會釋放鎖。但Thread類的suspend()方法已經(jīng)被廢棄。

到此這篇關(guān)于Java多線程中wait notify等待喚醒機制詳解的文章就介紹到這了,更多相關(guān)Java等待喚醒機制內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • SpringBoot配置文件properties和yml的實現(xiàn)

    SpringBoot配置文件properties和yml的實現(xiàn)

    本文主要介紹了SpringBoot配置文件properties和yml的實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-04-04
  • Java實現(xiàn)橋接方法isBridge()和合成方法isSynthetic()

    Java實現(xiàn)橋接方法isBridge()和合成方法isSynthetic()

    本文主要介紹了Java實現(xiàn)橋接方法isBridge()和合成方法isSynthetic(),文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-06-06
  • JAVA生成pdf文件的實操教程

    JAVA生成pdf文件的實操教程

    PDF是可移植文檔格式,是一種電子文件格式,具有許多其他電子文檔格式無法相比的優(yōu)點,下面這篇文章主要給大家介紹了關(guān)于JAVA生成pdf文件的相關(guān)資料,文中通過圖文介紹的非常詳細,需要的朋友可以參考下
    2022-11-11
  • default怎么修飾接口中的方法詳解

    default怎么修飾接口中的方法詳解

    今天給各位小伙伴們總結(jié)一下default怎么修飾接口中的方法,文中有非常詳細的圖文解說.對正在學(xué)習(xí)java的小伙伴們很有幫助,需要的朋友可以參考下
    2021-05-05
  • Spring bean不被GC的真正原因及分析

    Spring bean不被GC的真正原因及分析

    這篇文章主要介紹了Spring bean不被GC的真正原因及分析,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-04-04
  • java 使用ConcurrentHashMap和計數(shù)器實現(xiàn)鎖

    java 使用ConcurrentHashMap和計數(shù)器實現(xiàn)鎖

    這篇文章主要介紹了java 使用ConcurrentHashMap和計數(shù)器實現(xiàn)鎖的相關(guān)資料,需要的朋友可以參考下
    2017-05-05
  • SpringBoot項目使用MDC給日志增加唯一標(biāo)識的實現(xiàn)步驟

    SpringBoot項目使用MDC給日志增加唯一標(biāo)識的實現(xiàn)步驟

    本文介紹了如何在SpringBoot項目中使用MDC(Mapped?Diagnostic?Context)為日志增加唯一標(biāo)識,以便于日志追蹤,通過創(chuàng)建日志攔截器、配置攔截器以及修改日志配置文件,可以實現(xiàn)這一功能,文章還提供了源碼地址,方便讀者學(xué)習(xí)和參考,感興趣的朋友一起看看吧
    2025-03-03
  • JavaFX Application應(yīng)用實例

    JavaFX Application應(yīng)用實例

    下面小編就為大家?guī)硪黄狫avaFX Application應(yīng)用實例。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2016-10-10
  • Java的wait(), notify()和notifyAll()使用心得

    Java的wait(), notify()和notifyAll()使用心得

    本篇文章是對java的 wait(),notify(),notifyAll()進行了詳細的分析介紹,需要的朋友參考下
    2013-08-08
  • 詳解java中的PropertyChangeSupport與PropertyChangeListener

    詳解java中的PropertyChangeSupport與PropertyChangeListener

    這篇文章主要介紹了詳解java中的PropertyChangeSupport與PropertyChangeListener的相關(guān)資料,需要的朋友可以參考下
    2017-09-09

最新評論