何時要用join()方法?java
1,join方法是Thread類中的方法,主線程執行完start()方法,線程就進入就緒狀態,虛擬機最終會執行run方法進入運行狀態.此時.主線程跳出start方法往下執行jvm
2,兩個線程以上,當一個線程須要另外一個線程執行的結果時,能夠在該線程以前調用另外一個線程對象的join方法,以下:this
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結合能夠實現經典的生產者消費者模式.spa
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方法,這就實現了線程的順序執行,3d
碼解讀完畢code
從代碼中的while(isAlive){wait()}提及,對象
若是主線程在等待狀態,被其餘線程喚醒呢,因此前文我說,notify也通常配合synchronized,while使用,這個通常狀況下是消費者生產者模式,可是在jvm源碼中並沒blog
有這麼用,這是要等待t1線程結束前調用,因此這個isAlive就是保證必定被t1線程結束喚醒,而其餘線程喚醒while判斷以後再次進入掛起狀態.token
幾個問題的辨析,
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移入鎖池中參與競爭,等待池中的線程爲零,
公衆號: