線程中的join方法,與synchronized和wait()和notify()的關係

 

 

        何時要用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移入鎖池中參與競爭,等待池中的線程爲零,

 

公衆號:

相關文章
相關標籤/搜索