浅析Java的Thread.join函数

(一)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(*毫秒)主线程只是等待子进程*毫秒。

 

posted @ 2018-08-15 19:20  Wendy777  阅读(1878)  评论(2编辑  收藏  举报