java多線程join()方法的作用和實(shí)現(xiàn)原理解析(應(yīng)用場(chǎng)景)
1、join() 方法的作用
這個(gè)方法的作用是先將當(dāng)前線程掛起,待其他線程結(jié)束后在執(zhí)行當(dāng)前線程的代碼;
2、應(yīng)用場(chǎng)景
比如有三個(gè)人小紅、小李、小王, 三個(gè)人相約一起去酒店吃飯,菜已經(jīng)點(diǎn)好了, 三個(gè)人從不同的地方出發(fā),只有三個(gè)人都到了酒店之后才會(huì)開始上菜;那么這三個(gè)人就分別代表三個(gè)線程,這三個(gè)線程執(zhí)行完之后才會(huì)執(zhí)行 “上菜” 的代碼邏輯,
代碼示例
package com.Lock; /** * join方法示例 * 比如有三個(gè)人小紅、小李、小王, 三個(gè)人相約一起去酒店吃飯,菜已經(jīng)點(diǎn)好了, 三個(gè)人從不同的地方出發(fā),只有三個(gè)人都到了酒店之后才會(huì)開始上菜;那么這三個(gè)人就分別代表三個(gè)線程,這三個(gè)線程執(zhí)行完之后才會(huì)執(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() +"服務(wù)員開始上菜"); } } 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(), "小李"); // 三個(gè)人同時(shí)出發(fā) t1.start(); t2.start(); t3.start(); // 酒店也開始著手準(zhǔn)備好將要上的菜 new Thread(new Hotel2(t3),"酒店").start(); } }
打印結(jié)果
3、坑點(diǎn)
java的join方法中,這里有一個(gè)坑,就是下面這個(gè)方法
Thread.currentThread().join();
我們都知道 ,join方法的作用是阻塞,即等待線程結(jié)束,才繼續(xù)執(zhí)行。如果調(diào)用了Thread.currentThread().join(); 這個(gè)方法,那么線程一直在阻塞,無(wú)法終止。因?yàn)樗约涸诘却约航Y(jié)束;這無(wú)疑會(huì)造成死鎖;
接下來我們來測(cè)試一把,代碼和上面的一樣,只需要改一行代碼即可,在上面的代碼的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() +"服務(wù)員開始上菜"); } }
運(yùn)行后結(jié)果如下
所以這個(gè)方法一定不要直接使用;
4、join方法的實(shí)現(xiàn)原理
說了這么多,也舉了這么多例子了,join方法是怎么實(shí)現(xiàn)線程等待的呢?我點(diǎn)進(jìn)去join方法內(nèi)部可以看到,其實(shí)它的內(nèi)部也就是調(diào)用了wait()方法,
只不過它多做了一步,用了一個(gè)循環(huán)來判斷線程是否還活著,isAlive()方法就是用來判斷線程是否還活著;
4.1、join方法實(shí)現(xiàn)原理的疑問
那么這個(gè)時(shí)候肯定有的同學(xué)就有疑問了,只看到j(luò)oin()方法調(diào)用了wait()方法,但是沒看到有調(diào)用notify() 或者 notifyAll() 系列的喚醒方法,那它是怎么喚醒的呢?如果不喚醒,那不就一直等待下去了嗎?
原來啊,在java中,Thread類線程執(zhí)行完run()方法后,一定會(huì)自動(dòng)執(zhí)行notifyAll()方法
這是notifyAll()非常重要的隱藏知識(shí)點(diǎn),這個(gè)細(xì)節(jié)隱藏在Java的Native方法中,所以一般不會(huì)被人發(fā)現(xiàn)。我們觀察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í)行結(jié)束時(shí),此行代碼lock.notify_all(thread);
意思是通過notifyAll()
喚醒了所有等待線程。所以在使用線程的時(shí)候,要特別注意
到此這篇關(guān)于java多線程join()方法的作用和實(shí)現(xiàn)原理的文章就介紹到這了,更多相關(guān)java多線程join()內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Springboot實(shí)現(xiàn)ModbusTCP通信的示例詳解
ModbusTCP協(xié)議是Modbus由MODICON公司于1979年開發(fā),是一種工業(yè)現(xiàn)場(chǎng)總線協(xié)議標(biāo)準(zhǔn),本文主要介紹了Springboot實(shí)現(xiàn)ModbusTCP通信的相關(guān)知識(shí),需要的可以參考下2023-12-12Spring @Cacheable自定義緩存過期時(shí)間的實(shí)現(xiàn)示例
本文主要介紹了Spring @Cacheable自定義緩存過期時(shí)間的實(shí)現(xiàn)示例,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2024-05-05Spring?Data?JPA命名約定查詢實(shí)現(xiàn)方法
這篇文章主要為大家介紹了Spring?Data?JPA命名約定查詢實(shí)現(xiàn)方法示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-12-12Mybatis控制臺(tái)打印SQL語(yǔ)句的兩種方式實(shí)現(xiàn)
這篇文章主要介紹了Mybatis控制臺(tái)打印SQL語(yǔ)句的兩種方式實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-03-03SpringBoot實(shí)現(xiàn)RAS+AES自動(dòng)接口解密
本文主要介紹了SpringBoot實(shí)現(xiàn)RAS+AES自動(dòng)接口解密,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-03-03Java實(shí)現(xiàn)兩個(gè)日期相減等于天數(shù)
這篇文章主要介紹了Java兩個(gè)日期相減等于天數(shù)的實(shí)現(xiàn)方式,本文通過兩種方式結(jié)合實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-09-09Springboot啟動(dòng)同時(shí)創(chuàng)建數(shù)據(jù)庫(kù)和表實(shí)現(xiàn)方法
這篇文章主要介紹了Springboot啟動(dòng)同時(shí)創(chuàng)建數(shù)據(jù)庫(kù)和表,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)吧2023-01-01