1. 程式人生 > >執行緒方法join的簡單總結

執行緒方法join的簡單總結

雖然關於討論執行緒join方法的部落格已經很多了,不過個人感覺挺多都討論得不夠全面,所以我覺得有必要對其進行一個全面的總結。

  一、作用

  Thread類中的join方法的主要作用就是同步,它可以使得執行緒之間的並行執行變為序列執行。具體看程式碼:

public class JoinTest {
    public static void main(String [] args) throws InterruptedException {
        ThreadJoinTest t1 = new ThreadJoinTest("小明");
        ThreadJoinTest t2 = new ThreadJoinTest("小東");
        t1.start();
        /**join的意思是使得放棄當前執行緒的執行,並返回對應的執行緒,例如下面程式碼的意思就是:
         程式在main執行緒中呼叫t1執行緒的join方法,則main執行緒放棄cpu控制權,並返回t1執行緒繼續執行直到執行緒t1執行完畢
         所以結果是t1執行緒執行完後,才到主執行緒執行,相當於在main執行緒中同步t1執行緒,t1執行完了,main執行緒才有執行的機會
         */
        t1.join();
        t2.start();
    }

}
class ThreadJoinTest extends Thread{
    public ThreadJoinTest(String name){
        super(name);
    }
    @Override
    public void run(){
        for(int i=0;i<1000;i++){
            System.out.println(this.getName() + ":" + i);
        }
    }
}

上面程式結果是先列印完小明執行緒,在列印小東執行緒;  

上面註釋也大概說明了join方法的作用:在A執行緒中呼叫了B執行緒的join()方法時,表示只有當B執行緒執行完畢時,A執行緒才能繼續執行。注意,這裡呼叫的join方法是沒有傳參的,join方法其實也可以傳遞一個引數給它的,具體看下面的簡單例子:

public class JoinTest {
    public static void main(String [] args) throws InterruptedException {
        ThreadJoinTest t1 = new ThreadJoinTest("小明");
        ThreadJoinTest t2 = new ThreadJoinTest("小東");
        t1.start();
        /**join方法可以傳遞引數,join(10)表示main執行緒會等待t1執行緒10毫秒,10毫秒過去後,
         * main執行緒和t1執行緒之間執行順序由序列執行變為普通的並行執行
         */
        t1.join(10);
        t2.start();
    }

}
class ThreadJoinTest extends Thread{
    public ThreadJoinTest(String name){
        super(name);
    }
    @Override
    public void run(){
        for(int i=0;i<1000;i++){
            System.out.println(this.getName() + ":" + i);
        }
    }
}

上面程式碼結果是:程式執行前面10毫秒內列印的都是小明執行緒,10毫秒後,小明和小東程式交替列印。

所以,join方法中如果傳入引數,則表示這樣的意思:如果A執行緒中掉用B執行緒的join(10),則表示A執行緒會等待B執行緒執行10毫秒,10毫秒過後,A、B執行緒並行執行。需要注意的是,jdk規定,join(0)的意思不是A執行緒等待B執行緒0秒,而是A執行緒等待B執行緒無限時間,直到B執行緒執行完畢,即join(0)等價於join()。

  二、join與start呼叫順序問題

  上面的討論大概知道了join的作用了,那麼,入股 join在start前呼叫,會出現什麼後果呢?先看下面的測試結果

public class JoinTest {
    public static void main(String [] args) throws InterruptedException {
        ThreadJoinTest t1 = new ThreadJoinTest("小明");
        ThreadJoinTest t2 = new ThreadJoinTest("小東");
        /**join方法可以在start方法前呼叫時,並不能起到同步的作用
         */
        t1.join();
        t1.start();
        //Thread.yield();
        t2.start();
    }

}
class ThreadJoinTest extends Thread{
    public ThreadJoinTest(String name){
        super(name);
    }
    @Override
    public void run(){
        for(int i=0;i<1000;i++){
            System.out.println(this.getName() + ":" + i);
        }
    }
}

上面程式碼執行結果是:小明和小東執行緒交替列印。

所以得到以下結論:join方法必須線上程start方法呼叫之後呼叫才有意義。這個也很容易理解:如果一個執行緒都沒有start,那它也就無法同步了。

  三、join方法實現原理

  有了上面的例子,我們大概知道join方法的作用了,那麼,join方法實現的原理是什麼呢?

  其實,join方法是通過呼叫執行緒的wait方法來達到同步的目的的。例如,A執行緒中呼叫了B執行緒的join方法,則相當於A執行緒呼叫了B執行緒的wait方法,在呼叫了B執行緒的wait方法後,A執行緒就會進入阻塞狀態,具體看下面的原始碼:

public final synchronized void join(long millis)
    throws InterruptedException {
        long base = System.currentTimeMillis();
        long now = 0;

        if (millis < 0) {
            throw new IllegalArgumentException("timeout value is negative");
        }

        if (millis == 0) {
            while (isAlive()) {
                wait(0);
            }
        } else {
            while (isAlive()) {
                long delay = millis - now;
                if (delay <= 0) {
                    break;
                }
                wait(delay);
                now = System.currentTimeMillis() - base;
            }
        }
    }

從原始碼中可以看到:join方法的原理就是呼叫相應執行緒的wait方法進行等待操作的,例如A執行緒中呼叫了B執行緒的join方法,則相當於在A執行緒中呼叫了B執行緒的wait方法,當B執行緒執行完(或者到達等待時間),B執行緒會自動呼叫自身的notifyAll方法喚醒A執行緒,從而達到同步的目的。