1. 程式人生 > >淺析Java的Thread.join函數

淺析Java的Thread.join函數

比較 rgs 同步 自定義 測試 row exceptio throws illegal

(一)join參數解析

join(): 即join(0),主線程無限等待子進程結束,主線程方可執行。

join(long millis):主線程需等待子進程*毫秒,主線程方可執行。

(二)join源碼

join函數用了synchronized關鍵字,即為同步,線程安全。

   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 案例分析

自定義線程函數,方便查看結果比較函數效果

public class ThreadJoin extends Thread {
    public ThreadJoin(String name) {
        super(name);
    }

    @Override
    public void run(){
        for(int i=0;i<10;i++){
            System.out.println("i:" + i + ", name:" + this.getName());
        }
    }
}

【測試一】.線程無join()

public class ThreadJoinTest {
    public static void main(String [] args)throws InterruptedException {
        ThreadJoin threadOne = new ThreadJoin("曉東");
        ThreadJoin threadTwo = new ThreadJoin("小明");
        System.out.println("---Test start---");
        threadOne.start();
        threadTwo.start();
        System.out.println("---Test end---");
    }
}

運行結果:

---Test start---
---Test end---
i:0, name:曉東
i:0, name:小明
i:1, name:小明
i:2, name:小明
i:1, name:曉東
i:3, name:小明
i:4, name:小明
i:2, name:曉東
i:5, name:小明
i:6, name:小明
i:3, name:曉東
i:7, name:小明
i:4, name:曉東
i:5, name:曉東
i:6, name:曉東
i:7, name:曉東
i:8, name:曉東
i:9, name:曉東
i:8, name:小明
i:9, name:小明

總結:Main線程先執行,然後threadOne和threadTwo子進程隨機執行,當誰搶占CPU資源的時候,即執行System.out.println("i:" + i + ", name:" + this.getName())。由於線程搶占資源

不確定性,所以兩個子進程的執行順序不一,重復運行的結果也不一樣。

【測試二】.線程join()

public class ThreadJoinTest {
    public static void main(String [] args)throws InterruptedException {
        ThreadJoin threadOne = new ThreadJoin("曉東");
        ThreadJoin threadTwo = new ThreadJoin("小明");
        System.out.println("---Test start---");
        threadOne.start();
        threadTwo.start();
        threadOne.join();
        threadTwo.join();
        System.out.println("---Test end---");
    }
}

運行結果:

---Test start---
i:0, name:曉東
i:0, name:小明
i:1, name:小明
i:2, name:小明
i:1, name:曉東
i:3, name:小明
i:2, name:曉東
i:3, name:曉東
i:4, name:小明
i:4, name:曉東
i:5, name:小明
i:5, name:曉東
i:6, name:小明
i:6, name:曉東
i:7, name:曉東
i:8, name:曉東
i:9, name:曉東
i:7, name:小明
i:8, name:小明
i:9, name:小明
---Test end---

總結:可以看出Main主進程先執行start,然後執行兩個子進程,最後執行Main主進程的end。所以這個可以看出,主線程必須等到子進程都結束後,才執行。同樣子進程的運行順序也是不一樣的。

這裏需要註意下,子進程的join()是讓正在運行的父親進程(即Main主進程)進入等待,而不是new thread的子進程進入等待。即讓正在運行的進程進入等待。

【測試三】.線程join()在子進程開始運行之前和開始運行之後是否有差異

public class ThreadJoinTest {
    public static void main(String [] args)throws InterruptedException {
        ThreadJoin threadOne = new ThreadJoin("曉東");
        ThreadJoin threadTwo = new ThreadJoin("小明");

        System.out.println("---Test start---");
        threadOne.join();
        threadTwo.join();
        threadOne.start();
        threadTwo.start();
        System.out.println("---Test end---");
    }
}

運行結果:

---Test start---
---Test end---
i:0, name:曉東
i:0, name:小明
i:1, name:曉東
i:1, name:小明
i:2, name:曉東
i:3, name:曉東
i:2, name:小明
i:4, name:曉東
i:3, name:小明
i:4, name:小明
i:5, name:小明
i:5, name:曉東
i:6, name:小明
i:6, name:曉東
i:7, name:小明
i:7, name:曉東
i:8, name:小明
i:8, name:曉東
i:9, name:小明
i:9, name:曉東

總結:可以看出運行結果和無join()的【測試一】是一樣的。所以start()前執行join()無效,類似於只執行start()

【測試四】join()和join(**)區別

代碼一:

public class ThreadJoin extends Thread {
    public ThreadJoin(String name) {
        super(name);
    }

    @Override
    public void run(){
        for(int i=0;i<100;i++){
            System.out.println("i:" + i + ", name:" + this.getName());
        }
    }
}
public class ThreadJoinTest {
    public static void main(String [] args)throws InterruptedException {
        ThreadJoin threadOne = new ThreadJoin("曉東");
        ThreadJoin threadTwo = new ThreadJoin("小明");

        System.out.println("---Test start---");
        threadOne.start();
        threadTwo.start();
        threadOne.join(0);
        System.out.println("---Test end---");
    }
}

代碼二:

public class ThreadJoin extends Thread {
    public ThreadJoin(String name) {
        super(name);
    }

    @Override
    public void run(){
        for(int i=0;i<100;i++){
            System.out.println("i:" + i + ", name:" + this.getName());
        }
    }
}
public class ThreadJoinTest {
    public static void main(String [] args)throws InterruptedException {
        ThreadJoin threadOne = new ThreadJoin("曉東");
        ThreadJoin threadTwo = new ThreadJoin("小明");

        System.out.println("---Test start---");
        threadOne.start();
        threadTwo.start();
        threadOne.join(1);
        System.out.println("---Test end---");
    }
}

代碼二運行結果:

---Test start---
i:0, name:曉東
i:0, name:小明
i:1, name:小明
i:2, name:小明
i:1, name:曉東
i:3, name:小明
i:2, name:曉東
i:4, name:小明
i:3, name:曉東
i:5, name:小明
i:4, name:曉東
i:6, name:小明
i:5, name:曉東
i:7, name:小明
i:8, name:小明
i:9, name:小明
i:10, name:小明
i:6, name:曉東
i:11, name:小明
i:7, name:曉東
i:8, name:曉東
i:9, name:曉東
i:10, name:曉東
i:11, name:曉東
i:12, name:曉東
i:12, name:小明
i:13, name:曉東
i:13, name:小明
i:14, name:小明
i:15, name:小明
---Test end---
i:14, name:曉東
i:16, name:小明
i:15, name:曉東
i:17, name:小明
i:16, name:曉東
i:18, name:小明

...

運行結果:不一樣。

代碼一:先執行主線程start,然後執行兩個子線程,最後執行主線程end。

代碼二:先執行主線程start,然後執行兩個子線程部分,主線程睡眠1毫秒喚醒後,執行主線程end,再執行兩個子線程剩下部分。

總結:join()無限等待子進程結束, join(*毫秒)主線程只是等待子進程*毫秒。

淺析Java的Thread.join函數