Java Thread.join()

t.join() / t.join(long millis),当前线程里调用其它线程 t 的 join 方法,当前线程阻塞,但不释放对象锁,直到线程 t 执行完毕或者 millis 时间到,当前线程进入可运行状态。

t.join(); // 使调用线程 t 在此之前执行完毕
t.join(1000); // 等待 t 线程,等待时间是 1000 毫秒

JDK 源码:

public final void join() throws InterruptedException {  
    join(0);  
}

// 成员方法加了 synchronized 说明是 synchronized(this) 
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");  
    }  
    // 这里使用了 while 循环做判断,然后调用 wait 方法,所以说 join 方法的执行是完全通过 wait 方法实现的  
    // 等待时间为 0 的时候,就是无限等待,直到线程执行完了
    if (millis == 0) {  
        // 如果当前线程还存活的话,就等待  
        while (isAlive()) {  
            // 调用该线程的 join 方法的线程拿到锁之后进行等待,直到线程执行结束
            wait(0);  
        }  
    } else {  
        // 如果是等待的特定时间的话  
        while (isAlive()) {  
            long delay = millis - now;  
            if (delay <= 0) {  
                break;  
            }  
            wait(delay);  
            now = System.currentTimeMillis() - base;  
        }  
    }  
}  

从代码上看,如果线程被生成了,但还未被启动,调用它的 join() 方法是没有作用的,将直接继续向下执行

join 方法实现是通过 wait(Object 提供的方法)。当 main 线程调用 t.join() 之前,main 线程必须拥有线程对象 t 的锁,然后 main 线程调用 t.wait(time),直到等待时间 time 结束或者 t 线程执行完毕即 t.isAlive() == false

Example 1

public class JoinTest implements Runnable {  
      
    public static int a = 0;  
  
    public void run() {  
        for (int k = 0; k < 5; k++) {  
            a = a + 1;  
        }  
    }  
  
    public static void main(String[] args) throws Exception {  
        Runnable r = new JoinTest();  
        Thread t = new Thread(r);  
        t.start();        
        System.out.println(a);  
    }         
}  

程序的输出结果是 5 吗?答案是:有可能。其实你很难遇到输出 5 的时候,通常情况下都不是 5。为什么呢?当主线程 main 方法执行 System.out.println(a) 这条语句时,线程还没有真正开始运行,或许正在为它分配资源准备运行。因为为线程分配资源需要时间,而 main 方法执行完 t.start() 方法后继续往下执行 System.out.println(a),这时得到的结果是 a 还没有被改变的值 0 。怎样才能让输出结果为 5?其实很简单,join() 方法提供了这种功能。

public static void main(String[] args) throws Exception {  
    Runnable r = new JoinTest();  
    Thread t = new Thread(r);  
    t.start();        
    t.join();         // 加入 join()  
    System.out.println(a);  
}    

这个时候,程序输入结果始终为 5。

Example 2

class RunnableImpl implements Runnable {  
  
    public void run() {  
        try {  
            System.out.println("Begin sleep");  
            Thread.sleep(1000);  
            System.out.println("End sleep");  
        } catch (InterruptedException e) {  
            e.printStackTrace();  
        }  
  
    }  
}  

public class JoinTest {  
      
    public static void main(String[] args) {  
        Thread t = new Thread(new RunnableImpl());  
        t.start();  
        try {  
            t.join(1000);  
            System.out.println("joinFinish");  
        } catch (InterruptedException e) {  
            e.printStackTrace();       
        }  
    }  
}  

结果是:

Begin sleep
End sleep
joinFinish

当 main 线程调用 t.join 时,main 线程等待 t 线程,等待时间是 1000,如果 t 线程 Sleep 2000 呢

class RunnableImpl implements Runnable {  
  
    public void run() {  
        try {  
            System.out.println("Begin sleep");  
            Thread.sleep(2000);     // 原来为 1000  
            System.out.println("End sleep");  
        } catch (InterruptedException e) {  
            e.printStackTrace();  
        }  
  
    }  
}  

结果是:

Begin sleep
joinFinish
End sleep

也就是说 main 线程只等 1000 毫秒,不管 t 什么时候结束

Example 3

class CustomThread1 extends Thread {    
      
    public void run() {    
        String threadName = Thread.currentThread().getName();    
        System.out.println(threadName + " start.");    
        try {    
            for (int i = 0; i < 5; i++) {    
                System.out.println(threadName + " loop at " + i);    
                Thread.sleep(1000);    
            }    
            System.out.println(threadName + " end.");    
        } catch (Exception e) {    
            System.out.println("Exception from " + threadName + ".run");    
        }    
    }    
}    
  
class CustomThread extends Thread {    
    CustomThread1 t1;    
    public CustomThread(CustomThread1 t1) {            
        this.t1 = t1;    
    }    
    public void run() {    
        String threadName = Thread.currentThread().getName();    
        System.out.println(threadName + " start.");    
        try {    
            t1.join();    
            System.out.println(threadName + " end.");    
        } catch (Exception e) {    
            System.out.println("Exception from " + threadName + ".run");    
        }    
    }    
}    
  
public class JoinTestDemo {    
  
    public static void main(String[] args) {    
        String threadName = Thread.currentThread().getName();    
        System.out.println(threadName + " start.");    
        CustomThread1 t1 = new CustomThread1();    
        CustomThread t = new CustomThread(t1);    
        try {    
            t1.start();    
            Thread.sleep(2000);    
            t.start();    
            t.join();         // 下面会将此处注释掉    
        } catch (Exception e) {    
            System.out.println("Exception from main");    
        }    
        System.out.println(threadName + " end!");    
    }    
}   

结果:

main start. // main 方法所在的线程起动,但没有马上结束,因为调用 t.join(),所以要等到 t 结束了,此线程才能向下执行
[CustomThread1] Thread start. // 线程 CustomThread1 起动
[CustomThread1] Thread loop at 0 // 线程 CustomThread1 执行
[CustomThread1] Thread loop at 1 // 线程 CustomThread1 执行
[CustomThread] Thread start. // 线程 CustomThread 起动,但没有马上结束,因为调用 t1.join(),所以要等到 t1 结束了,此线程才能向下执行
[CustomThread1] Thread loop at 2 // 线程 CustomThread1 继续执行
[CustomThread1] Thread loop at 3 // 线程 CustomThread1 继续执行
[CustomThread1] Thread loop at 4 // 线程 CustomThread1 继续执行
[CustomThread1] Thread end. // 线程 CustomThread1 结束了
[CustomThread] Thread end. // 线程 CustomThread 在 t1.join() 阻塞处起动,向下继续执行的结果
main end! // 线程 CustomThread 结束,此线程在 t.join() 阻塞处起动,向下继续执行的结果

将上例中的 join 注释掉后结果为:

main start. // main 方法所在的线程起动,但没有马上结束,这里并不是因为 join 方法,而是因为 Thread.sleep(2000)
[CustomThread1] Thread start. // 线程 CustomThread1 起动
[CustomThread1] Thread loop at 0 // 线程 CustomThread1 执行
[CustomThread1] Thread loop at 1 // 线程 CustomThread1 执行
main end! // Thread.sleep(2000) 结束,虽然在线程 CustomThread 执行了 t1.join(),但这并不会影响到其他线程(这里 main 方法所在的线程)
[CustomThread] Thread start. // 线程 CustomThread 起动,但没有马上结束,因为调用 t1.join(),所以要等到 t1 结束了,此线程才能向下执行。
[CustomThread1] Thread loop at 2 // 线程 CustomThread1 继续执行
[CustomThread1] Thread loop at 3 // 线程 CustomThread1 继续执行
[CustomThread1] Thread loop at 4 // 线程 CustomThread1 继续执行
[CustomThread1] Thread end. // 线程 CustomThread1 结束了
[CustomThread] Thread end. // 线程 CustomThread 在 t1.join() 阻塞处起动,向下继续执行的结果

Example 4

main 线程调用 t.join 时,必须能够拿到线程 t 对象的锁,如果拿不到它是无法 wait 的,Example 2 中的
t.join(1000) 说明了 main 线程只等待 1 秒,但如果在它等待之前,其他线程获取了 t 对象的锁,它等待时间可不止 1 秒了

public class ThreadJoinTest {

    public static void main(String[] args) {
        Thread t = new Thread(new RunnableImpl());
        new ThreadTest(t).start();
        t.start();
        try {
            t.join(1000);           // main 线程等 1s
            System.out.println("join Finish");
        } catch (InterruptedException e) {
            e.printStackTrace();

        }
//        System.out.println("join Finish");
    }
}

class RunnableImpl implements Runnable {
    @Override
    public void run() {
        try {
            System.out.println("Thread Begin sleep " + System.currentTimeMillis());
            Thread.sleep(2000);
            System.out.println("Thread End sleep " + System.currentTimeMillis());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

class ThreadTest extends Thread {
    Thread thread;
    public ThreadTest(Thread thread) {
        this.thread = thread;
    }

    @Override
    public void run() {
        holdThreadLock();
    }

    public void holdThreadLock() {
        //用当前的线程当做lock
        synchronized (thread) {
            System.out.println("ThreadTest getObjectLock " + System.currentTimeMillis());
            try {
                Thread.sleep(9 * 1000);
            } catch (InterruptedException ex) {
                ex.printStackTrace();
            }
            System.out.println("ThreadTest ReleaseObjectLock " + System.currentTimeMillis());
        }

    }
}

注意:Thread.sleep() 会使当前运行的线程交出执行权。Thread.sleep(0) 的作用,就是 “触发操作系统立刻重新进行一次 CPU 竞争”。

结果是:

ThreadTest getObjectLock 1519645264719
Thread Begin sleep 1519645264719
Thread End sleep 1519645266719
ThreadTest ReleaseObjectLock 1519645273720
join Finish

或

Thread Begin sleep 1519644950436
ThreadTest getObjectLock 1519644950436
Thread End sleep 1519644952437
ThreadTest ReleaseObjectLock 1519644959438
join Finish

在 main 方法中,通过 new ThreadTest(t).start() 实例化 ThreadTest 线程对象,它通过 synchronized (thread) ,获取线程对象 t 的锁,并 sleep(9000) 后释放,这就意味着,即使 main 方法 t.join(1000) 等待一秒钟,它也必须等待 ThreadTest 线程释放 t 锁后才能进入 wait 方法中。

注意:t.join(1000) 是让 main 线程等待 1000ms 或 t 死掉后执行,所以 t.join(1000) 和 sleep(9000) 是同时的,ThreadTest 的实际等待时间还是 9s

最后编辑于 :
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

  • Java多线程学习 [-] 一扩展javalangThread类 二实现javalangRunnable接口 三T...
    影驰阅读 3,182评论 1赞 18
  • 本文主要讲了java中多线程的使用方法、线程同步、线程数据传递、线程状态及相应的一些线程函数用法、概述等。 首先讲...
    李欣阳阅读 2,670评论 1赞 15
  • 该文章转自:http://blog.csdn.net/evankaka/article/details/44153...
    加来依蓝阅读 7,513评论 3赞 87
  • 莲约 来自心灵的约会 莲 安静如湖水 与鱼相伴 我 欣然而至 在莲最美的今天 等了多久了 远处小桥那么安静 近处绿...
    文竹君Fan阅读 404评论 0赞 5
  • 作业要求:1)定义接口--定义查车速的方法2)实现接口--根据车的类型-输出不同的车速3)定义抽象类、抽象方法 -...
    灼灼2015阅读 391评论 0赞 0

友情链接更多精彩内容