日本综合一区二区|亚洲中文天堂综合|日韩欧美自拍一区|男女精品天堂一区|欧美自拍第6页亚洲成人精品一区|亚洲黄色天堂一区二区成人|超碰91偷拍第一页|日韩av夜夜嗨中文字幕|久久蜜综合视频官网|精美人妻一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問(wèn)題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營(yíng)銷解決方案
Thread的Join方法原理

本文轉(zhuǎn)載自微信公眾號(hào)「編了個(gè)程」,作者Yasin x 。轉(zhuǎn)載本文請(qǐng)聯(lián)系編了個(gè)程公眾號(hào)。

目前創(chuàng)新互聯(lián)已為近1000家的企業(yè)提供了網(wǎng)站建設(shè)、域名、網(wǎng)絡(luò)空間、網(wǎng)站托管維護(hù)、企業(yè)網(wǎng)站設(shè)計(jì)、兗州網(wǎng)站維護(hù)等服務(wù),公司將堅(jiān)持客戶導(dǎo)向、應(yīng)用為本的策略,正道將秉承"和諧、參與、激情"的文化,與客戶和合作伙伴齊心協(xié)力一起成長(zhǎng),共同發(fā)展。

Y說(shuō)

今天沒(méi)什么要說(shuō)的。我個(gè)人很喜歡拍天空的照片,放一張前段時(shí)間晚上拍的照片吧。

join方法釋放鎖嗎?

前段時(shí)間,有一個(gè)讀者私信我,問(wèn)了這么一個(gè)問(wèn)題:Thread實(shí)例的join方法內(nèi)部是調(diào)用的wait方法,而wait方法是會(huì)釋放鎖的,為什么網(wǎng)上很多文章(包括我們之前寫(xiě)的開(kāi)源書(shū)《深入淺出Java多線程》)會(huì)說(shuō)join方法不釋放鎖?

釋放thread對(duì)象鎖

我們先用書(shū)中的一個(gè)例子說(shuō)起:

 
 
 
 
  1. public class Join { 
  2.     static class ThreadA implements Runnable { 
  3.  
  4.         @Override 
  5.         public void run() { 
  6.             try { 
  7.                 System.out.println("我是子線程,我先睡一秒"); 
  8.                 Thread.sleep(1000); 
  9.                 System.out.println("我是子線程,我睡完了一秒"); 
  10.             } catch (InterruptedException e) { 
  11.                 e.printStackTrace(); 
  12.             } 
  13.         } 
  14.     } 
  15.  
  16.     public static void main(String[] args) throws InterruptedException { 
  17.         Thread thread = new Thread(new ThreadA()); 
  18.         thread.start(); 
  19.         thread.join(); 
  20.         System.out.println("如果不加join方法,我會(huì)先被打出來(lái),加了就不一樣了"); 
  21.     } 

在這個(gè)例子中,我們?cè)趍ain方法中調(diào)用了thread.join(),打印出來(lái)的效果就是:

 
 
 
 
  1. 我是子線程,我先睡一秒 
  2. 我是子線程,我睡完了一秒 
  3. 如果不加join方法,我會(huì)先被打出來(lái),加了就不一樣了 

這個(gè)例子想要表達(dá)的意圖很簡(jiǎn)單,就是通過(guò)thread實(shí)例的join方法,達(dá)到main線程等待thread線程執(zhí)行完后再繼續(xù)執(zhí)行的效果。

那join方法底層是如何實(shí)現(xiàn)這個(gè)功能的呢?究竟會(huì)不會(huì)釋放鎖呢?我們點(diǎn)進(jìn)去看看源碼。

 
 
 
 
  1. if (millis == 0) { 
  2.     while (isAlive()) { 
  3.         wait(0); 
  4.     } 
  5. } else { 
  6.     while (isAlive()) { 
  7.         long delay = millis - now; 
  8.         if (delay <= 0) { 
  9.             break; 
  10.         } 
  11.         wait(delay); 
  12.         now = System.currentTimeMillis() - base; 
  13.     } 

可以看到,join的底層是調(diào)用的wait(long)方法。而wait方法是Object類型的實(shí)例方法,會(huì)釋放當(dāng)前Object的鎖,且需要拿到當(dāng)前Object的鎖才行。

這么說(shuō)可能有點(diǎn)繞。眾所周知,「Java的鎖其實(shí)本質(zhì)上是對(duì)象鎖」,因?yàn)槲覀兦懊嬲{(diào)用的是thread.join(),所以這里的“鎖”對(duì)象其實(shí)thread這個(gè)對(duì)象。那這里wait釋放的是thread這個(gè)對(duì)象鎖。

我們把上面的main方法簡(jiǎn)單改一下,用另一個(gè)線程是占住thread這個(gè)對(duì)象鎖,就比較直觀了:

 
 
 
 
  1. public static void main(String[] args) throws InterruptedException { 
  2.     Thread thread = new Thread(new ThreadA()); 
  3.     thread.start(); 
  4.     new Thread(() -> { 
  5.         // 把thread對(duì)象作為鎖占住,這樣下面的join里面的wait只有等鎖釋放了才能執(zhí)行。 
  6.         synchronized (thread) { 
  7.             try { 
  8.                 System.out.println("我占住了thread鎖"); 
  9.                 Thread.sleep(10000); 
  10.                 System.out.println("我thread鎖釋放了"); 
  11.             } catch (InterruptedException e) { 
  12.                 e.printStackTrace(); 
  13.             } 
  14.         } 
  15.     }).start(); 
  16.     thread.join(); 
  17.     System.out.println("如果不加join方法,我會(huì)先被打出來(lái),加了就不一樣了"); 

打印結(jié)果:

 
 
 
 
  1. 我是子線程,我先睡一秒 
  2. 我占住了thread鎖 
  3. 我是子線程,我睡完了一秒 
  4. 我thread鎖釋放了 
  5. 如果不加join方法,我會(huì)先被打出來(lái),加了就不一樣了 

這就印證了那句話:wait方法執(zhí)行前,是需要獲取當(dāng)前對(duì)象的鎖的。

所以回歸到最開(kāi)始的問(wèn)題:join()方法會(huì)釋放鎖嗎?嚴(yán)瑾的答案是它會(huì)釋放thread實(shí)例的對(duì)象鎖,但不會(huì)釋放其它對(duì)象鎖(包括main線程)。stackoverflow也對(duì)這個(gè)有討論:Does Thread.join() release the lock? Or continue to hold it?。

簡(jiǎn)單來(lái)說(shuō),你說(shuō)它釋放了鎖也對(duì),因?yàn)樗_實(shí)通過(guò)wait方法釋放了thread對(duì)象鎖,你說(shuō)它沒(méi)釋放鎖也對(duì),因?yàn)閺恼{(diào)用線程的角度來(lái)看,它并沒(méi)有釋放當(dāng)前調(diào)用線程持有的對(duì)象鎖。

當(dāng)然,為了防止其它讀者看到這也有這個(gè)疑惑,我直接把文中的這句話刪掉了。

^image.png^

誰(shuí)喚醒了?

源碼看到這,我又有了一個(gè)新的疑問(wèn):join方法內(nèi)部是一個(gè)while循環(huán)。wait釋放了鎖,那必然會(huì)有一個(gè)人來(lái)喚醒它,程序才能夠繼續(xù)往下走。那必然有一個(gè)地方調(diào)用了thread對(duì)象的notify方法。

我們?cè)赥hread類里面可以找到一個(gè)exit()方法,上面?zhèn)渥?xiě)著:This method is called by the system to give a Thread a chance to clean up before it actually exits.

這么簡(jiǎn)單的英文大家應(yīng)該都能看懂吧?

里面有這么一段代碼:

 
 
 
 
  1. if (group != null) { 
  2.     group.threadTerminated(this); 
  3.     group = null; 
  4.  
  5. void threadTerminated(Thread t) { 
  6.     synchronized (this) { 
  7.         remove(t); 
  8.  
  9.         if (nthreads == 0) { 
  10.             notifyAll(); 
  11.         } 
  12.         if (daemon && (nthreads == 0) && 
  13.             (nUnstartedThreads == 0) && (ngroups == 0)) 
  14.         { 
  15.             destroy(); 
  16.         } 
  17.     } 

一開(kāi)始我以為是在這里喚醒的,但仔細(xì)一看,這里調(diào)用的對(duì)象是ThreadGroup的實(shí)例,而不是thread實(shí)例。所以應(yīng)該不是這個(gè)地方。

經(jīng)過(guò)一通google之后,我又在stackoverflow上找到了正確的答案(stackoverflow, yyds):who and when notify the thread.wait() when thread.join() is called?

答案顯示,這是在JVM層面去做的事:

 
 
 
 
  1. static void ensure_join(JavaThread* thread) { 
  2.   // We do not need to grap the Threads_lock, since we are operating on ourself. 
  3.   Handle threadObj(thread, thread->threadObj()); 
  4.   assert(threadObj.not_null(), "java thread object must exist"); 
  5.   ObjectLocker lock(threadObj, thread); 
  6.   // Ignore pending exception (ThreadDeath), since we are exiting anyway 
  7.   thread->clear_pending_exception(); 
  8.   // Thread is exiting. So set thread_status field in  java.lang.Thread class to TERMINATED. 
  9.   java_lang_Thread::set_thread_status(threadObj(), java_lang_Thread::TERMINATED); 
  10.   // Clear the native thread instance - this makes isAlive return false and allows the join() 
  11.   // to complete once we've done the notify_all below 
  12.   java_lang_Thread::set_thread(threadObj(), NULL); 
  13.   lock.notify_all(thread); 
  14.   // Ignore pending exception (ThreadDeath), since we are exiting anyway 
  15.   thread->clear_pending_exception(); 

可以看到除了notify_all以外,它其實(shí)做了很多掃尾的工作。包括處理異常、設(shè)置線程狀態(tài)等。

如果線程沒(méi)啟動(dòng)

再把代碼改一下,如果線程沒(méi)有通過(guò)start啟動(dòng)會(huì)怎樣呢?

 
 
 
 
  1. Thread thread = new Thread(new ThreadA()); 
  2. // thread.start(); 
  3. thread.join(); 
  4. System.out.println("如果不加join方法,我會(huì)先被打出來(lái),加了就不一樣了"); 

會(huì)直接執(zhí)行最后一行代碼打印出來(lái)。

看join源碼就知道了,在wait之前,會(huì)有一個(gè)isAlive()的判斷,看當(dāng)前線程是否是alive的。如果沒(méi)有start,那就會(huì)直接返回false,不進(jìn)入wait。

總結(jié)

join方法會(huì)釋放thread對(duì)象鎖,底層是wait方法,在JVM層面通過(guò)notify_all來(lái)喚醒的。


網(wǎng)站標(biāo)題:Thread的Join方法原理
標(biāo)題路徑:http://www.dlmjj.cn/article/ccosgpo.html