併發程式設計——執行緒中sleep(),yield(),join(),wait(),notify(),notifyAll()區別

超Code發表於2021-08-11

前言

今天簡單的講一講執行緒中sleep(),join(),yield(),wait(),notify(),notifyAll()這些方法的使用以及區別。
不過在講這些方法之前,需要簡單的介紹一下鎖池和等待池的概念。

專欄推薦:
併發程式設計專欄

鎖池和等待池

1.鎖池

所有需要競爭同步鎖的執行緒都會放在鎖池當中,比如當前物件的鎖已經被其中一個執行緒得到,則其他執行緒需要在這個鎖池進行等待,當前面的執行緒釋放同步鎖後鎖池中的執行緒去競爭同步鎖,當某個執行緒得到後會進入就緒佇列進行等待cpu資源分配。

2.等待池

當我們呼叫wait()方法後,執行緒會放到等待池當中,等待池的執行緒是不會去競爭同步鎖。只有呼叫了notify()或notifyAll()後等待池的執行緒才會開始去競爭鎖,notify()是隨機從等待池選出一個執行緒放到鎖池,而notifyAll()是將等待池的所有執行緒放到鎖池當中

sleep()

首先看一下sleep方法的原始碼,我們發現sleep是一個靜態方法,它接受一個long型別的毫秒值引數,而且是一個本地方法(native修飾),而且會丟擲InterruptedException(中斷異常)。

sleep()這個方法的使用很簡單,因為它是一個Thread的靜態方法,所以就可以直接Thread.sleep(毫秒值),休眠指定的毫秒數。

1、使當前執行緒(即呼叫該方法的執行緒)暫停執行一段時間,讓其他執行緒有機會執行。但是時間到了之後執行緒會進入就緒佇列,重新去競爭cpu資源。

2.sleep()會釋放cpu資源,但是不會釋放同步鎖(類鎖和物件鎖)

例如有兩個執行緒同時執行(沒有synchronized)一個執行緒優先順序為MAX_PRIORITY,另一個為MIN_PRIORITY,如果沒有Sleep()方法,只有高優先順序的執行緒執行完畢後,低優先順序的執行緒才能夠執行;但是高優先順序的執行緒sleep(500)後,低優先順序就有機會執行了。

總之,sleep()可以使低優先順序的執行緒得到執行的機會,當然也可以讓同優先順序、高優先順序的執行緒有執行的機會。

yield()

使當前正在執行的執行緒向另一個執行緒交出執行權。注意這是一個靜態方法。
該方法與sleep()類似,只是不能由使用者指定暫停多長時間,並且yield()方法只能讓同優先順序的執行緒有執行的機會。
1、yield()執行後執行緒直接進入就緒狀態。
2、yield()會釋放cpu資源,但是不會釋放同步鎖(類鎖和物件鎖)

join()

執行後執行緒進入阻塞狀態,例如線上程B中呼叫執行緒A的join(),那執行緒B會進入到阻塞佇列,直到join結束或中斷執行緒B才開始進入阻塞佇列。
可以實現一個執行緒的順序執行。

*下面舉一個小例子:
我排隊打飯,smile女神來了,我讓她先打飯,但是這個時候她男朋友來了,smile女神讓他男朋友先打飯。嗚嗚嗚~~~~
*

public class UseJoin {
   
    static class Smile implements Runnable {
        private Thread thread;

        public Smile(Thread thread) {
            this.thread = thread;
        }

        public Smile() {
        }

        public void run() {
            System.out.println("smile開始排隊打飯.....");
            try {
                if(thread!=null) thread.join();
            } catch (InterruptedException e) {
            }
            SleepTools.second(2);//休眠2秒
            System.out.println(Thread.currentThread().getName()
                    + " smile打飯完成.");
        }
    }

    static class SmileBoyfriend implements Runnable {

        public void run() {
            SleepTools.second(2);//休眠2秒
            System.out.println("smileBoyfriend開始排隊打飯.....");
            System.out.println(Thread.currentThread().getName()
                    + " smileBoyfriend打飯完成.");
        }
    }

    public static void main(String[] args) throws Exception {
        SmileBoyfriend smileBoyfriend = new SmileBoyfriend();
        Thread sbf = new Thread(smileBoyfriend);
        Smile smile = new Smile(sbf);
        Thread s = new Thread(smile);
        s.start();//我排隊打飯
        sbf.start();//smile女神來了,但是這個時候她男朋友來了,smile女神讓他男朋友先打飯
        System.out.println("chaoCode開始排隊打飯.....");
        s.join();
        Thread.sleep(2000);//讓主執行緒休眠2秒
        System.out.println(Thread.currentThread().getName() + " chaoCode打飯完成.");
    }
}

執行結果,可想而知,抱抱可憐的自己。

wait()和notify()、notifyAll()

1、wait方法用於協調多個執行緒對共享資料的存取,所以必須在Synchronized語句塊內使用
2、wait()方法使當前執行緒暫停執行並釋放會cpu資源,以及同步鎖(類鎖和物件鎖)
3、呼叫wait()後必須呼叫notify()或notifyAll()後執行緒才會從等待池進入到鎖池,當我們的執行緒競爭得到同步鎖後就會重新進入緒狀態等待cpu資源分配

當呼叫notify()方法後,將從物件的等待池中移走一個任意的執行緒並放到鎖標誌等待池中,只有鎖標誌等待池中執行緒能夠獲取鎖標誌;如果鎖標誌等待池中沒有執行緒,則notify()不起作用。

notifyAll()則從物件等待池中移走所有等待那個物件的執行緒並放到鎖標誌等待池中。

注意:

1、這三個方法都是java.lang.Object的方法。

2、notif()方法要配合wait()方法使用,一般在wait()之後呼叫或者線上程結束時呼叫才會成功。

感謝諸君的觀看,文中如有紕漏,歡迎在評論區來交流。如果這篇文章幫助到了你,歡迎點贊?關注。

相關文章