Thread源碼分析之join方法

join方法示例1

源碼

import java.util.concurrent.TimeUnit;


public class JoinWaitTest {

    private static int a = 0;
    private static int b = 100;

    public static void main(String... args) throws InterruptedException{
        Thread t = new Thread(new WaitThread());

        t.start();
        t.join();

        System.out.println("I'm waiting for WaitThread end.");
        System.out.println("The result is " + (a + b));
    }

    static class WaitThread implements Runnable {
        @Override
        public void run() {
            try {
                for (int i = 1; i < 6; i++) {
                    TimeUnit.SECONDS.sleep(1);
                    a++;
                    System.out.println(i);
                }
            } catch (InterruptedException e) {

            }
        }
    }
}

執行結果

1html

2java

3編程

4多線程

5app

I'm waiting for WaitThread end.ide

The result is 105oop

場景描述

在不少狀況下,主線程生成並啓動了子線程,若是子線程裏要進行大量的耗時的運算,主線程每每將於子線程以前結束,可是若是主線程處理完其餘的事務後,須要用到子線程的處理結果,也就是主線程須要等待子線程執行完成以後再結束,這個時候就要用到join()方法了。源碼分析

本例只是個示例,演示的就是上述的過程。this

join方法示例2

源碼

import java.util.concurrent.TimeUnit;


public class JoinTest {

    public static void main(String... args) throws InterruptedException {
        Thread jt = new Thread(new JoinThread());
        Thread tt = new Thread(new TimingThread());

        tt.start();
        tt.join();
        jt.start();

    }

    static class JoinThread implements Runnable {
        @Override
        public void run() {
            System.out.println("I have waited for too long.");
        }
    }

    static class TimingThread implements Runnable {
        @Override
        public void run() {
            for (int i = 0; i < 6; i++) {
                try {
                    TimeUnit.SECONDS.sleep(1);
                    System.out.println("Sleep end!");
                } catch (InterruptedException e) {

                }
            }
        }
    }
}

執行結果

Sleep end!spa

Sleep end!

Sleep end!

Sleep end!

Sleep end!

Sleep end!

I have waited for too long.

場景描述

跟源碼示例1大同小異,只不過此次是jt線程要等待tt線程結束。

join不帶參數源碼分析2--必須在同步方法/同步代碼塊中調用wait方法------爲何wait()【還有notify(),notifyAll()】必須在同步方法/代碼塊中調用

    在 Java中,全部對象都可以被做爲"監視器monitor"——指一個擁有一個獨佔鎖,一個入口隊列和一個等待隊列的實體entity。

  全部對象的非同步方法都可以在任意時刻被任意線程調用,此時不須要考慮加鎖的問題。

  而對於對象的同步方法來講,在任意時刻有且僅有一個擁有該對象獨佔鎖的線程可以調用它們。例如,一個同步方法是獨佔的。若是在線程調用某一對象的同步方法時,對象的獨佔鎖被其餘線程擁有,那麼當前線程將處於阻塞狀態,並添加到對象的入口隊列中。

    只有在調用線程擁有某個對象的獨佔鎖時,纔可以調用該對象的wait(),notify()和notifyAll()方法。若是嘗試在未獲取對象鎖時調用這三個方法,那麼你將獲得一個"java.lang.IllegalMonitorStateException:current thread not owner"。

    當一個線程正在某一個對象的同步方法中運行時調用了這個對象的wait()方法,那麼這個線程將釋放該對象的獨佔鎖並被放入這個對象的等待隊列,(JZ:意味着其餘線程也能夠再次調用同一個對象的wait方法)

  注意,wait()方法強制當前線程釋放對象鎖。這意味着線程在調用某對象的wait()方法以前,當前線程必須已經得到該對象的鎖。所以,線程必須在某個對象的同步方法或同步代碼塊中才能調用該對象的wait()方法。wait方法是native方法,裏面的實現細節不清楚,可是線程A調用某對象B的wait方法確定默認線程A已經擁有了某對象B的鎖(可以進入同步方法或同步代碼塊就說明線程A競爭到了對象鎖)。若是跟這種默認(競爭獲得鎖)衝突,那麼JVM就會報出運行時錯誤IllegalMonitorStateException(RuntimeException)

  當某線程調用某對象的 notify()或notifyAll()方法時,任意一個(對於notify())或者全部(對於notifyAll())在該對象的等待隊列中的線程,將被轉移到該對象的入口隊列。接着這些隊列(譯者注:可能只有一個)將競爭該對象的鎖,最終得到鎖的線程繼續執行。

若是沒有線程在該對象的等待隊列中等待得到鎖,那麼notify()和notifyAll()將不起任何做用。在調用對象的notify()和notifyAll()方法以前,調用線程必須已經獲得該對象的鎖。所以,必須在某個對象的同步方法或同步代碼塊中才能調用該對象的notify()或notifyAll()方法。

  對於處於某對象的等待隊列中的線程,只有當其餘線程調用此對象的notify()或notifyAll()方法時纔有機會繼續執行。

  調用wait()方法的緣由一般是,調用線程但願某個特殊的狀態(或變量)被設置以後再繼續執行。調用notify()或notifyAll()方法的緣由一般是,調用線程但願告訴其餘等待中的線程:"特殊狀態已經被設置"。這個狀態做爲線程間通訊的通道,它必須是一個可變的共享狀態(或變量)。

例如,生產者線程向緩衝區中寫入數據,消費者線程從緩衝區中讀取數據。消費者線程須要等待直到生產者線程完成一次寫入操做。生產者線程須要等待消費者線程完成一次讀取操做。假設wait(),notify(),notifyAll()方法不須要加鎖就可以被調用。

此時消費者線程調用wait()正在進入狀態變量的等待隊列(譯者注:可能還未進入)。在同一時刻,生產者線程調用notify()方法打算向消費者線程通知狀態改變。那麼此時消費者線程將錯過這個通知並一直阻塞。所以,對象的wait(),notify(),notifyAll()方法必須在該對象的同步方法或同步代碼塊中被互斥地調用。

[JZ]:這也是多線程編程裏的一個經典問題,線程A進行鎖操做的過程是非原子的,線程B就進行了釋放的請求,致使線程A申請鎖後沒法釋放!

參考網頁

http://www.cnblogs.com/jiangz222/p/4719671.html

http://www.blogjava.net/freeman1984/archive/2011/10/14/361306.html

join方法源碼分析

jdk源碼

/**

     * Waits at most {@code millis} milliseconds for this thread to

     * die. A timeout of {@code 0} means to wait forever.

     *

     * <p> This implementation uses a loop of {@code this.wait} calls

     * conditioned on {@code this.isAlive}. As a thread terminates the

     * {@code this.notifyAll} method is invoked. It is recommended that

     * applications not use {@code wait}, {@code notify}, or

     * {@code notifyAll} on {@code Thread} instances.

     *

     * @param  millis

     *         the time to wait in milliseconds

     *

     * @throws  IllegalArgumentException

     *          if the value of {@code millis} is negative

     *

     * @throws  InterruptedException

     *          if any thread has interrupted the current thread. The

     *          <i>interrupted status</i> of the current thread is

     *          cleared when this exception is thrown.

     */

    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不帶參數源碼分析1--實質上是調用了wait方法

join()方法實質上就是join(0),最終執行代碼以下所示

實質上調用代碼1就至關於調用代碼2

join帶參數源碼分析

核心代碼

變一下:

while (isAlive()) {

            if (millis <= now) {

                break;

            }

            wait(millis - now);

            now = System.currentTimeMillis() - base;

        }

分析

base和now都是時間。base是剛執行代碼時的時間,now是執行代碼流逝的時間。判斷mills時間是否流逝完畢,流逝完畢則break跳出代碼。

相關文章
相關標籤/搜索