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

Java線程間協(xié)作wait、notify和notifyAll詳解

 更新時間:2023年10月26日 10:00:12   作者:chengmaoning  
這篇文章主要介紹了Java線程間協(xié)作wait、notify和notifyAll詳解,在 Java 中可以用 wait、notify 和 notifyAll 來實現(xiàn)線程間的通信,盡管關于wait和notify的概念很基礎,它們也都是Object類的函數(shù),但用它們來寫代碼卻并不簡單,,需要的朋友可以參考下

概要描述

在 Java 中可以用 wait、notify 和 notifyAll 來實現(xiàn)線程間的通信。盡管關于wait和notify的概念很基礎,它們也都是Object類的函數(shù),但用它們來寫代碼卻并不簡單。

wait, notify, notifyAll 都是基類Object的方法,而不屬于Thread,這讓習慣了調用Thread.sleep()使線程阻塞的同學感到奇怪。不過這樣設計是有道理的,因為這些方法操作的鎖(monitor)也是對象的一部分??梢姡csleep不同,通過調用共享對象的wait方法使當前線程等待;通過調用對象的notify, notifyAll 方法喚醒該對象上的等待線程。

先來看官方文檔,Java doc對wait方法的描述:

public final void wait() throws InterruptedException

Causes the current thread to wait until another thread invokes the notify() method or the notifyAll() method for this object. In other words, this method behaves exactly as if it simply performs the call wait(0).

The current thread must own this object's monitor. The thread releases ownership of this monitor and waits until another thread notifies threads waiting on this object's monitor to wake up either through a call to the notify method or the notifyAll method. The thread then waits until it can re-obtain ownership of the monitor and resumes execution.

As in the one argument version, interrupts and spurious wakeups are possible, and this method should always be used in a loop:

     synchronized (obj) {
         while (<condition does not hold>)
             obj.wait();
         ... // Perform action appropriate to condition
     }

This method should only be called by a thread that is the owner of this object's monitor. See the notify method for a description of the ways in which a thread can become the owner of a monitor.

Throws:
    IllegalMonitorStateException - if the current thread is not the owner of the object's monitor.
    InterruptedException - if any thread interrupted the current thread before or while the current thread was waiting for a notification. The interrupted status of the current thread is cleared when this exception is thrown. 

Java doc對notify的描述:

public final void notify()

Wakes up a single thread that is waiting on this object's monitor. If any threads are waiting on this object, one of them is chosen to be awakened. The choice is arbitrary and occurs at the discretion of the implementation. A thread waits on an object's monitor by calling one of the wait methods.

The awakened thread will not be able to proceed until the current thread relinquishes the lock on this object. The awakened thread will compete in the usual manner with any other threads that might be actively competing to synchronize on this object; for example, the awakened thread enjoys no reliable privilege or disadvantage in being the next thread to lock this object.

This method should only be called by a thread that is the owner of this object's monitor. A thread becomes the owner of the object's monitor in one of three ways:

    By executing a synchronized instance method of that object.
    By executing the body of a synchronized statement that synchronizes on the object.
    For objects of type Class, by executing a synchronized static method of that class. 

Only one thread at a time can own an object's monitor.

Throws:
    IllegalMonitorStateException - if the current thread is not the owner of this object's monitor.

生產者-消費者示例:

/**
 * 
 */
public class Producer extends Thread {

    private volatile Queue<Integer> queue;
    private int maxSize;

    public Producer(Queue<Integer> queue, int maxSize) {
        this.queue = queue;
        this.maxSize = maxSize;
    }

    @Override
    public void run() {
        while (true) {
            //wait,notify方法必須在同步代碼中運行
            synchronized (queue) {
                //條件一定在循環(huán)中判斷,以防死鎖
                while (queue.size() == maxSize) {
                    try {                             System.out.println(Thread.currentThread().getName() + " wait.");
                        queue.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                int i = new Random().nextInt();
                System.out.println(Thread.currentThread().getName() + " produce: " + i);
                queue.add(i);
                queue.notifyAll();
            }
        }
    }
}

消費者:

 /**
 * 
 */
public class Consumer extends Thread {

    private volatile Queue<Integer> queue;
    private int maxSize;

    public Consumer(Queue<Integer> queue, int maxSize) {
        this.queue = queue;
        this.maxSize = maxSize;
    }

    @Override
    public void run() {
        while (true) {
            synchronized (queue) {
                while (queue.isEmpty()) {
                    try {
                        System.out.println(Thread.currentThread().getName() + " wait.");
                        queue.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

                System.out.println(Thread.currentThread().getName() + " consume: " + queue.remove());
                queue.notifyAll();
            }
        }
    }
}

main方法:

/**
 * 
 */
public class WaitNotifyMain {

    private volatile static Queue<Integer> queue = new LinkedList<>();

    /**
     * @param args
     */
    public static void main(String[] args) {

        Producer producer = new Producer(queue, 10);
        producer.setName("Producer");//設置線程名稱
        Consumer consumer = new Consumer(queue, 10);
        consumer.setName("Consumer");

        producer.start();
        consumer.start();

    }

}

打印輸出:

Producer produce: -2017386252
Producer produce: 1186339917
Producer produce: -674757828
Producer produce: 605757848
Producer produce: -539314860
Producer produce: 490590935
Producer produce: -845855520
Producer produce: -1459720588
Producer produce: 1274488529
Producer produce: 55225134
Producer wait.
Consumer consume: -2017386252
Consumer consume: 1186339917
Consumer consume: -674757828
Consumer consume: 605757848
Consumer consume: -539314860
Consumer consume: 490590935
Consumer consume: -845855520
Consumer consume: -1459720588
Consumer consume: 1274488529
Consumer consume: 55225134
Consumer wait.
Producer produce: 673397316
Producer produce: -1176693368
Producer produce: -1707265532
Producer produce: -1614197913
Producer produce: 306171031
Producer produce: -1646438955
Producer produce: 1141572321
Producer produce: 1235215288
Producer produce: -692805724
Producer produce: -2131184778
Producer wait.
Consumer consume: 673397316
Consumer consume: -1176693368

總結

wait, notify, notifyAll 是共享對象上的調用,而不是線程對象的調用。

wait, notify, notifyAll一定要在共享對象同步方法或同步代碼塊中執(zhí)行,否則會在運行時拋出IllegalMonitorStateException的異常。因為wait, notify, notifyAll包含了對共享對象鎖的操作,所以之前一定要先synchronized獲取對象鎖。

在共享對象上調用wait()時,當前線程進入等待狀態(tài), 并釋放剛獲取的對象鎖(Thread.sleep()是不釋放鎖的),讓出CPU, 此時,其他線程可以調用共享對象的同步方法或代碼塊。

喚醒線程在共享對象上執(zhí)行notify會隨機喚醒該對象的其中之一等待線程;喚醒線程在共享對象上執(zhí)行notifyAll會喚醒該對象上的所有等待線程;這里要著重注意兩點

A,喚醒線程執(zhí)行完共享對象的notify或notifyAll方法后,仍然要執(zhí)行完synchronized修飾的同步代碼塊中后面的代碼才能釋放對象鎖,因此,通常notify后面盡量減少執(zhí)行代碼,讓對象鎖盡快釋放。

B, 喚醒是指線程ready的狀態(tài),尚未運行,共享對象的鎖被喚醒線程釋放后,ready狀態(tài)的線程跟普通線程一樣需要競爭共享對象的鎖,執(zhí)行同步代碼塊中wait()后面的代碼。

永遠在循環(huán)(loop)里調用 wait 和 notify, notifyAll,不是在 If 語句,避免死鎖情況發(fā)生。

基于以上認知,下面這個是使用wait和notify函數(shù)的規(guī)范代碼模板:

// The standard idiom for calling the wait method in Java 
synchronized (sharedObject) { 
    while (condition) { 
    sharedObject.wait(); 
        // (Releases lock, and reacquires on wakeup) 
    } 
    // do action based upon condition e.g. take or put into queue 
}

在while循環(huán)里使用wait的目的,是在線程被喚醒的前后都持續(xù)檢查條件是否被滿足。如果條件并未改變,wait被調用之前notify的喚醒通知就來了,那么這個線程并不能保證被喚醒,有可能會導致死鎖問題。

到此這篇關于Java線程間協(xié)作wait、notify和notifyAll詳解的文章就介紹到這了,更多相關Java線程間協(xié)作內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • 解決springboot中自定義JavaBean返回的json對象屬性名稱大寫變小寫問題

    解決springboot中自定義JavaBean返回的json對象屬性名稱大寫變小寫問題

    開發(fā)過程中發(fā)現(xiàn)查詢返回的數(shù)據(jù)出現(xiàn)自定義的JavaBean的屬性值大小寫格式出現(xiàn)問題,導致前端無法接受到數(shù)據(jù),目前有四種解決方法,根據(jù)大佬的經驗之談,前兩種是最簡單便捷的,后兩種是比較通用的方法,需要的朋友可以參考下
    2023-10-10
  • 基于JVM性能監(jiān)控命令介紹

    基于JVM性能監(jiān)控命令介紹

    下面小編就為大家?guī)硪黄贘VM性能監(jiān)控命令介紹。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-09-09
  • java實現(xiàn)簡易點菜器

    java實現(xiàn)簡易點菜器

    這篇文章主要為大家詳細介紹了java實現(xiàn)簡易點菜器,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2019-12-12
  • spring boot整合RabbitMQ(Direct模式)

    spring boot整合RabbitMQ(Direct模式)

    springboot集成RabbitMQ非常簡單,如果只是簡單的使用配置非常少,springboot提供了spring-boot-starter-amqp項目對消息各種支持。下面通過本文給大家介紹下spring boot整合RabbitMQ(Direct模式),需要的朋友可以參考下
    2017-04-04
  • 使用Apache Ignite實現(xiàn)Java數(shù)據(jù)網格

    使用Apache Ignite實現(xiàn)Java數(shù)據(jù)網格

    今天我們來探討如何使用Apache Ignite來實現(xiàn)Java數(shù)據(jù)網格,Apache Ignite是一個高性能的內存計算平臺,它提供了分布式緩存、數(shù)據(jù)網格和計算功能,可以顯著提高大規(guī)模應用的數(shù)據(jù)處理性能,感興趣的小伙伴跟著小編一起來看看吧
    2024-08-08
  • Spring Boot與Docker部署實踐

    Spring Boot與Docker部署實踐

    這篇文章主要介紹了Spring Boot與Docker部署實踐,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-05-05
  • SpringBoot整合Kaptcha實現(xiàn)圖片驗證碼加減乘除功能

    SpringBoot整合Kaptcha實現(xiàn)圖片驗證碼加減乘除功能

    在開發(fā)Web應用時,驗證碼是一個常見的功能,它可以幫助我們防止機器人的惡意操作,今天我們將學習如何使用Kaptcha生成圖片驗證碼,并自定義驗證碼內容為100以內的加減乘除運算,感興趣的朋友跟隨小編一起看看吧
    2024-07-07
  • Java 無參數(shù)構造函數(shù)的應用

    Java 無參數(shù)構造函數(shù)的應用

    本篇文章主要介紹了Java 無參數(shù)構造函數(shù)的應用,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-04-04
  • win10和win7下java開發(fā)環(huán)境配置教程

    win10和win7下java開發(fā)環(huán)境配置教程

    這篇文章主要為大家詳細介紹了win7下Java開發(fā)環(huán)境配置教程,win10下Java開發(fā)環(huán)境配置,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-06-06
  • Java?詳解如何從尾到頭打印鏈表

    Java?詳解如何從尾到頭打印鏈表

    在我們平時的代碼過程中,鏈表是我們經常遇到的一個數(shù)據(jù)結構,它非常的簡單,但Java并不能直接將一個鏈表打印出來,通過這篇文章我們來講解一下這個問題
    2022-01-01

最新評論