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

你可能感兴趣的:(Java Thread.join())