
什么時候要用join()方法?
1,join方法是Thread類中的方法,主線程執行完start()方法,線程就進入就緒狀態,虛擬機最終會執行run方法進入運行狀態.此時.主線程跳出start方法往下執行
2,兩個線程以上,當一個線程需要另一個線程執行的結果時,可以在該線程之前調用另一個線程對象的join方法,如下:
public class TestThread { public static void main(String[] args) throws InterruptedException { Thread t1 = new ThreadA("ThreadA"); Thread t2 = new Thread(new ThreadB(),"ThreadB"); t1.start();//主線程調用start方法,開啟t1線程, t1.join();//主線程調用join方法,獲取對象t1對象鎖,掛起主線程.直到t1線程結束 t2.start();//主線程調用start方法,開啟t2線程 t2.join();//主線程調用join,獲取t2對象鎖,掛起主線程,直到t2線程結束 System.out.println(Thread.currentThread().getName()+"主線程結束"); } }
如上代碼,主線程一路下來調用其他線程的對象的join方法,就會被掛起直到該線程對象所在的線程結束.從而實現線程的順序執行.
查看join的源碼,該方法是同步方法,鎖是t1線程所在對象的對象鎖,我們可以看到,wait總是在synchronized代碼塊里面使用,並且在while循環中,有wait()就必然有notify()/notifyAll(),所以同樣的一般情況下notify()/notifyAll()也是在synchronized代碼塊中的while使用的,synchronized,while,wait,notify結合可以實現經典的生產者消費者模式.
public final synchronized void join(long millis) throws InterruptedException { long base = System.currentTimeMillis(); .... //主線程調用join方法就擁有了t1對象的鎖,wait(0)一直處於等待狀態, //直到某個線程調用了該對象的notify或者notifyAll() if (millis == 0) { while (isAlive()) { wait(0); } } else { .... } }
在join方法中我們看到了wait(),那么一定在某個地方有notify方法,該方法則在jvm源碼中,這個段源碼的作用是結束線程,在線程退出前需要做一些動作,其中就有調用notify_all的動作,看注釋,還將isAlive變為false,
// 位於/hotspot/src/share/vm/runtime/thread.cpp中 void JavaThread::exit(bool destroy_vm, ExitType exit_type) { // ... // Notify waiters on thread object. This has to be done after exit() is called // on the thread (if the thread is the last thread in a daemon ThreadGroup the // group should have the destroyed bit set before waiters are notified). // 有一個賊不起眼的一行代碼,就是這行 ensure_join(this); // ... } static void ensure_join(JavaThread* thread) { // We do not need to grap the Threads_lock, since we are operating on ourself. Handle threadObj(thread, thread->threadObj()); assert(threadObj.not_null(), "java thread object must exist"); ObjectLocker lock(threadObj, thread); // Ignore pending exception (ThreadDeath), since we are exiting anyway thread->clear_pending_exception(); // Thread is exiting. So set thread_status field in java.lang.Thread class to TERMINATED. java_lang_Thread::set_thread_status(threadObj(), java_lang_Thread::TERMINATED); // Clear the native thread instance - this makes isAlive return false and allows the join() // to complete once we've done the notify_all below java_lang_Thread::set_thread(threadObj(), NULL); // 喚醒其他線程 lock.notify_all(thread); // Ignore pending exception (ThreadDeath), since we are exiting anyway thread->clear_pending_exception(); }
由此我們即看到了notify,也看到了isAlive變為false,所以主線程再掛起的時候,會等待該對象線程t1結束並調用notify,喚醒之后則跳出循環檢測isAlive為false則跳
出循環結束join方法,這就實現了線程的順序執行,
碼解讀完畢
從代碼中的while(isAlive){wait()}說起,
如果主線程在等待狀態,被其他線程喚醒呢,所以前文我說,notify也一般配合synchronized,while使用,這個一般情況下是消費者生產者模式,但是在jvm源碼中並沒
有這么用,這是要等待t1線程結束前調用,所以這個isAlive就是保證一定被t1線程結束喚醒,而其他線程喚醒while判斷之后再次進入掛起狀態.
幾個問題的辨析,
1,線程之間的競爭是同一個對象鎖的競爭或者同一個類鎖的競爭,與其他對象的鎖無關.其他線程搶別的對象的鎖,執行別對象的wait/notify方法都與競爭本對象
鎖的線程無關.
2, 鎖分為對象鎖,類鎖,鎖的使用者叫線程,鎖攔截的對象也是線程,不是什么方法,或者其他,.
對象鎖,表示對象里面所有被synchronized(this)修飾的代碼塊和synchronized 修飾的普通方法塊,不能同時被兩個線程執行.詳情見:synchronized的使用方法和作用域:
https://mp.weixin.qq.com/s?__biz=MzI4NTEzMjc5Mw==&mid=2650554746&idx=1&sn=8e45e741ca9f058dba1f3ebbea0e9f07&chksm=f3f833ecc48fbafa295e355c1cdd52dc4259f161dafdc1703d181a5e9f4f76563c98493bd221&token=2005887224&lang=zh_CN#rd
類鎖是被synchronized(MyClass.class)和static synchronized 修飾的靜態方法,不能同時被兩個線程執行,詳情見:synchronized的使用方法和作用域
https://mp.weixin.qq.com/s?__biz=MzI4NTEzMjc5Mw==&mid=2650554746&idx=1&sn=8e45e741ca9f058dba1f3ebbea0e9f07&chksm=f3f833ecc48fbafa295e355c1cdd52dc4259f161dafdc1703d181a5e9f4f76563c98493bd221&token=2005887224&lang=zh_CN#rd
3,線程釋放鎖的場景,
a,執行完同步代碼塊,
b,在執行同步代碼塊的過程中,遇到異常而導致線程終止,鎖也會被釋放
c在執行同步代碼塊的過程中,執行了鎖所屬對象的wait方法,這個線程會釋放對象鎖,而此線程對象會進入線程等待池中,等待被喚醒
notify和notifyAll的區別
我們首先得明確,一個對象只有一個鎖,參與該對象鎖競爭的線程,與參與其他對象鎖競爭的線程無關.線程競爭,對應一個對象的一個鎖.
有n個線程參與競爭同一對象的鎖,t1率先搶到了鎖,鎖池中有n-1個線程在等待競爭鎖,t1調用了wait()方法t1線程掛起,等待池中有一線程等待被喚醒.t2線程,獲得了鎖,鎖池中有n-2個線程等待競爭鎖,t2線程調用了wait方法掛起,等待池中有兩個線程等待被喚醒,
此時t3線程獲得了鎖,
如果t3線程在同步代碼塊中調用的是notify則只會隨機喚醒等待池中的一個線程移入鎖池中參與鎖競爭,有可能是t1,也有可能是t2,等待池中的線程為1
如果t3線程在同步代碼塊中調用的notifyAll,則會同時喚醒t1,t2移入鎖池中參與競爭,等待池中的線程為零,
公眾號:

