1. 程式人生 > >(四)wait()、notify()、notifyAll()方法||wait()與sleep()的區別

(四)wait()、notify()、notifyAll()方法||wait()與sleep()的區別

wait()、notify()、notifyAll()方法

wait()與sleep()的區別

一、wait()、notify()、notifyAll()方法

方法介紹

wait()、notify()、notifyAll()是三個定義在Object類裡的方法,可以用來控制執行緒的狀態。

這三個方法最終呼叫的都是jvm級的native方法。隨著jvm執行平臺的不同可能有些許差異。
如果物件呼叫了wait方法就會使持有該物件的執行緒把該物件的控制權交出去,然後處於等待狀態。
如果物件呼叫了notify方法就會通知某個正在等待這個物件的控制權的執行緒可以繼續執行。
如果物件呼叫了notifyAll方法

就會通知所有等待這個物件控制權的執行緒繼續執行。

注意:這三個方法一定要線上程同步中使用,並且是同一個鎖的資源

思考上個文章裡面出現的問題(三)

/**
* 為何打印出來的資料不是小紅一個、大聖一個;而是批量的小紅以及批量的大聖?
* 解決辦法:
* 生產者執行緒生產一個,消費者馬上消費,消費者沒有消費完畢,生產者不能繼續生產;
* 生產者沒有生產,消費者不能讀;
*/

程式碼實戰

共享資源源實體類

/**
 共享資源源實體類
 */
class UserInfo {
    public String userSex;
    public String userName;
    /**執行緒通訊標識
     * true:讓生產者進行等待,消費者進行消費
     * false:生產者可以生產,消費者進行等待
     * */
    public boolean flag=false;
}

輸入執行緒資源

public class InputThread extends Thread{
    private UserInfo user;
    //建構函式
public InputThread (UserInfo user){ this.user=user; } @Override public void run() { int count=0; while (true){ //解決執行緒不安全問題,在出現執行緒安全的程式碼塊上加synchronized關鍵字 synchronized (user) { //當flag為true時(注意:在判斷條件裡if(user.flag)等價於if(user.flag==true)) if (user.flag) { try { //讓當前執行緒 從執行狀態變為休眠狀態,並且釋放鎖的資源 user.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } //1.當執行緒第一次執行時,flag為false,先進行生產 if(count==0){ user.userName="大聖"; user.userSex="男"; }else{ user.userName="小紅"; user.userSex="女"; } //計算奇數或者偶數公式 count=(count+1)%2; //2.當第一個執行緒生產完的時候,把flag狀態改為true, // 即讓生產者執行緒處於等待狀態,讓消費者進行讀取 user.flag=true; //喚醒當前等待的執行緒,否則你只能列印一條 user.notify(); } } }

輸出執行緒資源

public class OutputThread extends Thread{

    private UserInfo user;
    //建構函式
    public OutputThread(UserInfo user){
        this.user=user;
    }
    @Override
    public void run() {
        while (true) {
            //解決執行緒不安全問題,在出現執行緒安全的程式碼塊上加synchronized關鍵字
            synchronized (user) {
                //當flag為false時(注意:在判斷條件裡if(!user.flag)等價於if(user.flag==false))
                if (!user.flag) {
                    try {
                        //讓當前執行緒 從執行狀態變為休眠狀態,並且釋放鎖的資源
                        user.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                //3.進來時,flag為true,消費者進行消費,生產者進行等待
                System.out.println("userName:"+user.userName+"----userSex:"+user.userSex);
                //4.消費者讀取完後,把flag狀態改為false,再去生產者那邊生產,待在消費者這裡也沒用
                user.flag=false;
                //喚醒當前等待的執行緒,否則你只能列印一條
                user.notify();
            }
        }
    }

}

執行程式碼:

 UserInfo user=new UserInfo();
        //構造方法就是在這裡用的
        //兩個執行緒共享同一變數
        InputThread it=new InputThread(user);
        OutputThread ot=new OutputThread(user);
        it.start();
        ot.start();

執行結果:
這裡寫圖片描述

執行結果正常:偶數列印列印大聖,奇數列印小紅,不再批量列印了

二、wait與sleep的區別是什麼?

相同點:

  • 都是做休眠

不同點:

  1. 這兩個方法來自不同的類分別是,sleep來自Thread類,和wait來自Object類。

    sleep是Thread的靜態類方法,誰呼叫的誰去睡覺,即使在a執行緒裡呼叫了b的sleep方法,實際上還是a去睡覺,要讓b執行緒睡覺要在b的程式碼中呼叫sleep。

  2. 最主要是sleep方法沒有釋放鎖,而wait方法釋放了鎖,使得其他執行緒可以使用同步控制塊或者方法。

    sleep不出讓系統資源;wait是進入執行緒等待池等待,出讓系統資源,其他執行緒可以佔用CPU。一般wait不會加時間限制,因為如果wait執行緒的執行資源不夠,再出來也沒用,要等待其他執行緒呼叫notify/notifyAll喚醒等待池中的所有執行緒,才會進入就緒佇列等待OS分配系統資源。

    sleep(milliseconds)可以用時間指定使它自動喚醒過來,如果時間不到只能呼叫interrupt()強行打斷。

    Thread.Sleep(0)的作用是“觸發作業系統立刻重新進行一次CPU競爭”。

  3. 使用範圍:wait,notify和notifyAll只能在同步控制方法或者同步控制塊裡面使用,而sleep可以在任何地方使用

    synchronized(x){
    x.notify()
    //或者wait()
    }

  4. sleep必須捕獲異常,而wait,notify和notifyAll不需要捕獲異常