【java線程】的wait、sleep、join、yied


1.概述

使用Thread相對來說比較簡單,沒有什么成本,但是通常來說,我們使用線程基本就是覆寫run方法,然后調用線程對象的start函數啟動線程。

對於面試人員來說,這些都不是面試官會問到的問題,而線程的wait、sleep、join、yied這幾個函數可問的就比較多了。

 

 

函數名 作用
wait    

當一個線程執行到wait()方法時,它就進入到一個和該對象相關的等待池中,同時失去(釋放)了對象的機鎖,使得其他線程可以訪問。用戶可以使用notify、notifyAll或者指定睡眠時間來喚醒當前等待池中的線程。

注意:wait()、notify()、notifyAll()必須放在synchronized block中,否則會拋出異常

sleep    該函數是Thread的靜態函數,作用是使調用線程進入睡眠狀態。因為sleep()是Thread類的Static方法,因此他不能改變對象的機鎖。所以,當在一個Sychronized塊中調用sleep()方法,線程雖然休眠了,但是對象的機鎖並沒有釋放,其他線程無法訪問這個對象(即使睡着也持有對象鎖)
join 等待目標線程執行完成之后再繼續執行
yield 線程禮讓。目標線程由運行狀態轉換為就緒狀態,也就是讓出執行權限,讓其他線程得以優先執行,但其他線程能否優先執行是未知數

 

 

 2.實際運用

 (1)wait、notify

public class ThreadFunTest {
    private static Object sLockObject = new Object();

    static void waitAndNotifyAll() {
        System.out.println("主線程運行");
        Thread thread = new WaitThread();
        thread.start();
        long startTime = System.currentTimeMillis();
        try {
            synchronized (sLockObject) {
                System.out.println("主線程等待");
                sLockObject.wait();
            }
        } catch (Exception ignored) {

        }
        long timsMs = (System.currentTimeMillis() - startTime);
        System.out.println("主線程繼續-->等待耗時:" + timsMs + "ms");
    }

    static class WaitThread extends Thread {
        @Override
        public void run() {
            try {
                synchronized (sLockObject) {
                    Thread.sleep(3000);
                    sLockObject.notifyAll();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        ThreadFunTest.waitAndNotifyAll();
    }
}

執行結果: 

 

在waitAndNotifyAll()函數中,會啟動一個WaitThread線程,在該線程中將會調用sleep函數睡眠3秒,線程啟動后在主線程調用sLockObject的wait函數,使主線程進入等待狀態,此時將不會繼續執行。等WaitThread在run函數睡眠3秒后會調用sLockObject的notifyAll函數,此時就會重新喚醒正在等待中的主線程,因此會繼續執行下去。

 wait、notify機制通常用於等待機制的實現,當條件為滿足時調用wait進入等待狀態,一旦條件滿足,調用notify或notifyAll喚醒等待的線程繼續執行。

(2)join

與wait、sleep的淺顯易懂不同的是join的作用並不那么直觀,也許就是這個原因,使得join函數並不那么容易理解。join函數的原始解釋為”Blocks the current Thread(Thread.currentThread())until the receiver finishes its execution and dies“,意思就是阻塞當前調用join函數時所在的線程,直到接收線程執行完畢后再繼續。

 

static void joinDemo() {
        Worker worker1 = new Worker("work-1");
        Worker worker2 = new Worker("work-2");
        worker1.start();
        System.out.println("啟動線程1");
        try{
            //調用work1的join函數,主線程會阻塞直到work1執行完成
            worker1.join();
            System.out.println("啟動線程2");
            //再啟動線程2,並且調用線程2的join函數,主線程會阻塞直到worker2執行完成
            worker2.start();
            worker2.join();
        }catch (Exception ignored){

        }
        System.out.println("主線程繼續執行");
    }

    static class Worker extends Thread {
        public Worker(String name) {
            super(name);
        }

        @Override
        public void run() {
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("work in " + getName());
        }
    }

    public static void main(String[] args) {
        ThreadFunTest.joinDemo();
    }

執行結果:

這是因為再joinDemo函數中,首先創建了兩個子線程,然后啟動了worker1,下一步再調用worker1的join函數。此時主線程進入阻塞狀態,一直到worker1執行完畢后才開始繼續執行。因為Worker的run函數會睡眠2秒,因此,在主線程中每次調用join實際上都會阻塞2秒,直到run執行完畢再繼續。所以,上述邏輯為啟動線程1--等待線程1執行完成--啟動線程2--等待線程2執行完成--繼續執行主干代碼。

(3)yied

yied函數與join類似,官方解釋為”Causes the calling Thread to yield execution time to another Thread that is read to run“。意思是使調用該函數的線程讓出執行時間給其他已經就緒狀態的線程。我們知道,線程的執行是有時間片的,每個線程輪流占用CPU固定的時間,執行周期到了之后就讓出執行權給其他線程。而yield的功能就是主動讓出線程的執行權給其他線程,其他線程能否得到優先執行就得看各個線程的狀態了。示例:

static void yieldDemo() {
        YieldThread t1 = new YieldThread("thread-1");
        YieldThread t2 = new YieldThread("thread-2");
        t1.start();
        t2.start();
    }

    static class YieldThread extends Thread {
        public YieldThread(String name) {
            super(name);
        }
        public synchronized void run() {
            for (int i = 0; i < 5; i++) {
                System.out.println(String.format("%s [%d] ---> %d", this.getName(), this.getPriority(), i));
                //當i為2時調用當前線程的yield函數
                if (i == 2) {
                    Thread.yield();
                }
            }
        }
    }

    public static void main(String[] args) {
        ThreadFunTest.yieldDemo();
    }

執行結果:

 

 通常情況下t1首先執行,讓t1的run函數執行到i等於2時讓出當前線程的執行時間,因此,我們看到執行到的thread-2  等於2之前交替執行,等於2時,thread-2讓出執行時間,執行thread-1,thread-1執行到2時又將執行權讓出,執行thread-2直到完畢再開始執行thread-1.

調用yield就是讓出當前線程的執行權,這樣一來就王其他線程得到優先執行。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM