java多線程join()方法的作用和實現原理解析(應用場景)
1、join() 方法的作用
這個方法的作用是先將當前線程掛起,待其他線程結束后在執(zhí)行當前線程的代碼;

2、應用場景
比如有三個人小紅、小李、小王, 三個人相約一起去酒店吃飯,菜已經點好了, 三個人從不同的地方出發(fā),只有三個人都到了酒店之后才會開始上菜;那么這三個人就分別代表三個線程,這三個線程執(zhí)行完之后才會執(zhí)行 “上菜” 的代碼邏輯,

代碼示例
package com.Lock;
/**
* join方法示例
* 比如有三個人小紅、小李、小王, 三個人相約一起去酒店吃飯,菜已經點好了, 三個人從不同的地方出發(fā),只有三個人都到了酒店之后才會開始上菜;那么這三個人就分別代表三個線程,這三個線程執(zhí)行完之后才會執(zhí)行 “上菜” 的代碼邏輯,
*/
public class ConutDownLatchJoinDemo implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "開始出發(fā)了");
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 {
// 待其他線程執(zhí)行完成后在執(zhí)行下面的代碼
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(), "小李");
// 三個人同時出發(fā)
t1.start();
t2.start();
t3.start();
// 酒店也開始著手準備好將要上的菜
new Thread(new Hotel2(t3),"酒店").start();
}
}
打印結果

3、坑點
java的join方法中,這里有一個坑,就是下面這個方法
Thread.currentThread().join();
我們都知道 ,join方法的作用是阻塞,即等待線程結束,才繼續(xù)執(zhí)行。如果調用了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()方法,

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

4.1、join方法實現原理的疑問
那么這個時候肯定有的同學就有疑問了,只看到join()方法調用了wait()方法,但是沒看到有調用notify() 或者 notifyAll() 系列的喚醒方法,那它是怎么喚醒的呢?如果不喚醒,那不就一直等待下去了嗎?
原來啊,在java中,Thread類線程執(zhí)行完run()方法后,一定會自動執(zhí)行notifyAll()方法
這是notifyAll()非常重要的隱藏知識點,這個細節(jié)隱藏在Java的Native方法中,所以一般不會被人發(fā)現。我們觀察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);
//下行執(zhí)行了notifyAll()操作
lock.notify_all(thread);
thread -> clear_pending_exception();
}
其中ensure_join就是執(zhí)行join()方法,等方法執(zhí)行結束時,此行代碼lock.notify_all(thread);意思是通過notifyAll()喚醒了所有等待線程。所以在使用線程的時候,要特別注意
到此這篇關于java多線程join()方法的作用和實現原理的文章就介紹到這了,更多相關java多線程join()內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Mybatis-plus自定義SQL注入器查詢@TableLogic邏輯刪除后的數據詳解
這篇文章主要給大家介紹了關于Mybatis-plus自定義SQL注入器查詢@TableLogic邏輯刪除后的數據的相關資料,文中通過實例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下2023-03-03
SpringBoot使用CXF集成WebService的方法
這篇文章主要介紹了SpringBoot使用CXF集成WebService的方法,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2019-08-08
MyBatis使用<foreach>標簽like查詢報錯解決問題
這篇文章主要介紹了MyBatis使用<foreach>標簽like查詢報錯解決問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-03-03
在lambda的foreach遍歷中break退出操作(lambda foreach break)
這篇文章主要介紹了在lambda的foreach遍歷中break退出操作(lambda foreach break),具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-09-09
Java SpringMVC 異常處理SimpleMappingExceptionResolver類詳解
這篇文章主要介紹了SpringMVC 異常處理SimpleMappingExceptionResolver類詳解,本篇文章通過簡要的案例,講解了該項技術的了解與使用,以下就是詳細內容,需要的朋友可以參考下2021-09-09

