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

友情链接更多精彩内容