java多線程join()方法的作用和實現原理解析(應用場景)

1、join() 方法的作用

    這個方法的作用是先將當前線程掛起,待其他線程結束後在執行當前線程的代碼;

2、應用場景

比如有三個人小紅、小李、小王, 三個人相約一起去酒店吃飯,菜已經點好瞭, 三個人從不同的地方出發,隻有三個人都到瞭酒店之後才會開始上菜;那麼這三個人就分別代表三個線程,這三個線程執行完之後才會執行 “上菜” 的代碼邏輯,

代碼示例

package com.Lock;
 
/**
 * join方法示例
 * 比如有三個人小紅、小李、小王, 三個人相約一起去酒店吃飯,菜已經點好瞭, 三個人從不同的地方出發,隻有三個人都到瞭酒店之後才會開始上菜;那麼這三個人就分別代表三個線程,這三個線程執行完之後才會執行 “上菜” 的代碼邏輯,
 */
public class ConutDownLatchJoinDemo implements Runnable {
 
 
    @Override
    public void run() {
 
        System.out.println(Thread.currentThread().getName() + "開始出發瞭");
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + "到酒店瞭");
 
 
    }
}
 
// 酒店線程
class Hotel2 implements  Runnable{
 
    Thread thread;
 
    public Hotel2 ( Thread thread){
        this.thread = thread;
    }
    @Override
    public void run() {
 
        System.out.println(Thread.currentThread().getName() +"正在等待大傢的到來.....");
        try {
            // 待其他線程執行完成後在執行下面的代碼
            thread.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("人齊瞭,"+Thread.currentThread().getName() +"服務員開始上菜");
    }
}
class Main2 {
    public static void main(String[] args) throws InterruptedException {
 
        Thread t1 = new Thread(new ConutDownLatchJoinDemo(), "小紅");
        Thread t2 = new Thread(new ConutDownLatchJoinDemo(), "小王");
        Thread t3 = new Thread(new ConutDownLatchJoinDemo(), "小李");
 
        // 三個人同時出發
        t1.start();
        t2.start();
        t3.start();
 
        // 酒店也開始著手準備好將要上的菜
        new Thread(new Hotel2(t3),"酒店").start();
 
 
    }
}

打印結果

3、坑點

java的join方法中,這裡有一個坑,就是下面這個方法

Thread.currentThread().join();

我們都知道 ,join方法的作用是阻塞,即等待線程結束,才繼續執行。如果調用瞭Thread.currentThread().join(); 這個方法,那麼線程一直在阻塞,無法終止。因為它自己在等待自己結束;這無疑會造成死鎖;

接下來我們來測試一把,代碼和上面的一樣,隻需要改一行代碼即可,在上面的代碼的Hotel.run()方法中,將 t3.join(); 改為 Thread.currentThread().join(); 即可;

// 酒店線程
class Hotel2 implements  Runnable{
 
    Thread thread;
 
    public Hotel2 ( Thread thread){
        this.thread = thread;
    }
    @Override
    public void run() {
 
        System.out.println(Thread.currentThread().getName() +"正在等待大傢的到來.....");
        try {
            //  將  t3.join(); 改為 Thread.currentThread().join();
            Thread.currentThread().join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("人齊瞭,"+Thread.currentThread().getName() +"服務員開始上菜");
    }
}

運行後結果如下

所以這個方法一定不要直接使用;

4、join方法的實現原理

    說瞭這麼多,也舉瞭這麼多例子瞭,join方法是怎麼實現線程等待的呢?我點進去join方法內部可以看到,其實它的內部也就是調用瞭wait()方法,

隻不過它多做瞭一步,用瞭一個循環來判斷線程是否還活著,isAlive()方法就是用來判斷線程是否還活著;

4.1、join方法實現原理的疑問

那麼這個時候肯定有的同學就有疑問瞭,隻看到join()方法調用瞭wait()方法,但是沒看到有調用notify() 或者 notifyAll() 系列的喚醒方法,那它是怎麼喚醒的呢?如果不喚醒,那不就一直等待下去瞭嗎?

原來啊,在java中,Thread類線程執行完run()方法後,一定會自動執行notifyAll()方法

 這是notifyAll()非常重要的隱藏知識點,這個細節隱藏在Java的Native方法中,所以一般不會被人發現。我們觀察C/C++源碼,如下:

oid JavaThread::exit(booldestory_vm, ExitTypeexit_type);
static void ensure_join(JavaThread*thread) {
	Handle threadObj(thread, thread -> threadObj());
	ObjectLocker lock(threadObj, thread);
	thread -> clear_pending_exception();
	java_lang_Thread::set_thread_status(threadObj(), java_lang_Thread::TERMINATED);
	java_lang_Thread::set_thread(threadObj(), NULL);
     //下行執行瞭notifyAll()操作
	lock.notify_all(thread);
	thread -> clear_pending_exception();
}

其中ensure_join就是執行join()方法,等方法執行結束時,此行代碼lock.notify_all(thread);意思是通過notifyAll()喚醒瞭所有等待線程。所以在使用線程的時候,要特別註意 

到此這篇關於java多線程join()方法的作用和實現原理的文章就介紹到這瞭,更多相關java多線程join()內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!

推薦閱讀: