Thread 的 wait、sleep、join、yied 理解


 

一.查看API

sleep是Thread類的方法,導致此線程暫停執行指定時間,給其他線程執行機會,但是依然保持着監控狀態,過了指定時間會自動恢復,調用sleep方法不會釋放鎖對象。

當調用sleep方法后,當前線程進入阻塞狀態。目的是讓出CPU給其他線程運行的機會。但是由於sleep方法不會釋放鎖對象,所以在一個同步代碼塊中調用這個方法后,線程雖然休眠了,但其他線程無法訪問它的鎖對象。這是因為sleep方法擁有CPU的執行權,它可以自動醒來無需喚醒。而當sleep()結束指定休眠時間后,這個線程不一定立即執行,因為此時其他線程可能正在運行。

wait方法是Object類里的方法,當一個線程執行到wait()方法時,它就進入到一個和該對象相關的等待池中,同時釋放了鎖對象,等待期間可以調用里面的同步方法,其他線程可以訪問,等待時不擁有CPU的執行權,否則其他線程無法獲取執行權。當一個線程執行了wait方法后,必須調用notify或者notifyAll方法才能喚醒,而且是隨機喚醒,若是被其他線程搶到了CPU執行權,該線程會繼續進入等待狀態。由於鎖對象可以時任意對象,所以wait方法必須定義在Object類中,因為Obeject類是所有類的基類。

二.是否可以傳入參數

sleep()方法必須傳入參數,參數就是休眠時間,時間到了就會自動醒來。

wait()方法可以傳入參數也可以不傳入參數,傳入參數就是在參數結束的時間后開始等待,不穿如參數就是直接等待。

三.是否需要捕獲異常

sleep方法必須要捕獲異常,而wait方法不需要捕獲異常。sleep方法屬於Thread類中方法,表示讓一個線程進入睡眠狀態,等待一定的時間之后,自動醒來進入到可運行狀態,不會馬上進入運行狀態,因為線程調度機制恢復線程的運行也需要時間,一個線程對象調用了sleep方法之后,並不會釋放他所持有的所有對象鎖,所以也就不會影響其他進程對象的運行。但在sleep的過程中過程中有可能被其他對象調用它的interrupt(),產生InterruptedException異常,如果你的程序不捕獲這個異常,線程就會異常終止,進入TERMINATED狀態,如果你的程序捕獲了這個異常,那么程序就會繼續執行catch語句塊(可能還有finally語句塊)以及以后的代碼。

wait屬於Object的成員方法,一旦一個對象調用了wait方法,必須要采用notify()和notifyAll()方法喚醒該進程;如果線程擁有某個或某些對象的同步鎖,那么在調用了wait()后,這個線程就會釋放它持有的所有同步資源,而不限於這個被調用了wait()方法的對象。wait()方法也同樣會在wait的過程中有可能被其他對象調用interrupt()方法而產生。

四.作用范圍

wait、notify和notifyAll方法只能在同步方法或者同步代碼塊中使用,而sleep方法可以在任何地方使用。但是注意sleep是靜態方法,也就是說它只對當前對象有效。通過對象名.sleep()想讓該對象線程進入休眠是無效的,它只會讓當前線程進入休眠。

五.調用者的區別

首先為什么wait、notify和notifyAll方法要和synchronized關鍵字一起使用?

因為wait方法是使一個線程進入等待狀態,並且釋放其所持有的鎖對象,notify方法是通知等待該鎖對象的線程重新獲得鎖對象,然而如果沒有獲得鎖對象,wait方法和notify方法都是沒有意義的,因此必須先獲得鎖對象再對鎖對象進行進一步操作於是才要把wait方法和notify方法寫到同步方法和同步代碼塊中了。

由此可知,wait和notify、notifyAll方法是由確定的對象即鎖對象來調用的,鎖對象就像一個傳話的人,他對某個線程說停下來等待,然后對另一個線程說你可以執行了(實質上是被捕獲了),這一過程是線程通信。sleep方法是讓某個線程暫停運行一段時間,其控制范圍是由當前線程決定,運行的主動權是由當前線程來控制(擁有CPU的執行權)。

其實兩者的區別都是讓線程暫停運行一段時間,但本質的區別一個是線程的運行狀態控制,一個是線程間的通信。

 

 

 

函數名 作用
wait    

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

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

 

wait()方法可以傳入參數也可以不傳入參數,傳入參數就是在參數結束的時間后開始等待,不穿如參數就是直接等待。

wait方法不需要捕獲異常

sleep   

該函數是Thread的靜態函數,作用是使調用線程進入睡眠狀態。因為sleep()是Thread類的Static方法,因此他不能改變對象的鎖。所以,當在一個Sychronized塊中調用sleep()方法,線程雖然休眠了,但是對象的鎖並沒有釋放,其他線程無法訪問這個對象(即使睡着也持有對象鎖)

 

sleep()方法必須傳入參數,參數就是休眠時間,時間到了就會自動醒來。

sleep方法必須要捕獲異常

join 等待目標線程執行完成之后再繼續執行
yield 線程禮讓。目標線程由運行狀態轉換為就緒狀態,也就是讓出執行權限,讓其他線程得以優先執行,但其他線程能否優先執行是未知數

package com.adao.thread.yeild;

public 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) {
        YieldThread t1 = new YieldThread("thread-1");
        YieldThread t2 = new YieldThread("thread-2");
        t1.start();
        t2.start();
    }
}

結果:

 

 取消屏蔽:

 

 我們看到執行到的thread-2  等於2之前交替執行,等於2時,thread-2讓出執行時間,執行thread-1,thread-1執行到2時又將執行權讓出

 JOIN

package com.adao.thread.yeild;

public 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) {
        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("主線程繼續執行");
    }
}

 

 

Wait:

package com.adao.thread.yeild;

public class Wait {
    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) {
        Wait.waitAndNotifyAll();
    }
}

 


免責聲明!

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



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