Thread 的 join() 方法


 
线程模块,计划用一个系列博文整理总结。上一篇博文:  《 线程系列1:创建线程
 
 

1、join() 方法说明

 

join() 是Thread的实例方法,官方解释为:等待该线程终止。
 
join() 方法的作用就是:将调用join的线程优先执行,当前正在执行的线程阻塞,直到调用join方法的线程执行完毕
 
或者被打断,主要用于线程之间的交互。

 
“Java 7 Concurrency Cookbook” 对 join() 方法的定义:

public final void join()throws InterruptedException: Waits for this thread to die.

        thread.join() 方法阻塞调用此方法的线程(calling thread),直到线程thread完成,此线程再继续;通常用于在
 
main() 主线程内,等待其它线程完成再结束 main() 主线程。

        另外还有一个应用场景:如何实现让线程T1,T2,T3,在T1执行完成后才执行T2,T2执行完成后才执行T3,
 
也就是线程的串行化,通过 Thread 类的 join() 方法就可以实现

 

2、代码演示

 
代码演示,主要是对比,使用 join()方法前后效果对比。
 
创建线程 Thread-a 代码:


/**
 * @title: ThreadA
 * @description: TODO
 * @date 2019/6/6 
 */
public class ThreadA implements Runnable{

    private String threadName;

    private Thread thread;

    // 构造方法中就启用线程
    ThreadA(String threadName) {
        this.threadName = threadName;
        thread = new Thread(this, threadName);
        thread.start();
    }

    @Override
    public void run() {
        System.out.println("线程" + Thread.currentThread().getName() + "业务逻辑开始!");
       for(int i = 6; i > 0 ; i--) {
         // 线程睡眠1秒
         try {
             Thread.sleep(1000);
             System.out.println(Thread.currentThread().getName() + " : " + i);
         }  catch (Exception e) {
             System.out.println(Thread.currentThread().getName() + "线程执行异常!");
             e.printStackTrace();
         }
       }
       System.out.println(Thread.currentThread().getName() + " 线程结束!");
    }

    public String getThreadName() {
        return threadName;
    }

    public Thread getThread() {
        return thread;
    }
}

2.1 添加 join() 方法后

 
测试代码一:


/**
 * @title: ThreadMain
 * @description: TODO
 * @date 2019/6/6 
 */
public class ThreadMain {


    public static void main(String[] args) throws InterruptedException {

       // 创建线程 B
       Thread threadB = new Thread(new Runnable() {
            @Override
            public void run() {
                // 启动线程 Thread-a
                ThreadA threadA = new ThreadA("Thread-a");
                try {
                    // 加入 join 方法
                    threadA.getThread().join();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                // 线程B的业务逻辑
                System.out.println("开始线程 " + Thread.currentThread().getName() + "业务逻辑");
                for (int i = 6; i > 0 ; i--) {
                    try {
                        System.out.println(Thread.currentThread().getName() + " : " + i);
                        // 睡眠0.5秒
                        Thread.sleep(500);
                    } catch (Exception e) {
                        System.out.println(Thread.currentThread().getName() + " 线程执行异常!");
                        e.printStackTrace();
                    }
                }

                System.out.println(Thread.currentThread().getName() + " 线程结束!");

            }
       }, "Thread-B");

       // 启动线程B
       threadB.start();
       // 加入 join 方法
       threadB.join();

        // 主线程业务逻辑开始
        System.out.println("主线程业务逻辑开始");
        for (int i = 6; i > 0 ; i--) {
            try {
                System.out.println(Thread.currentThread().getName() + " : " + i);
                // 睡眠0.3秒
                Thread.sleep(300);
            } catch (Exception e) {
                System.out.println(Thread.currentThread().getName() + " 线程执行异常!");
                e.printStackTrace();
            }
        }

        System.out.println(Thread.currentThread().getName() + " 线程结束!");
    }
}

 
运行结果一:
在这里插入图片描述
 

2.2 添加 join() 方法前

 
测试代码二,仅将测试代码一中的,两次调用 join() 方法部分注释掉,其他部分均一样。
 
测试代码二:


/**
 * @title: ThreadMain
 * @description: TODO
 * @date 2019/6/6 
 */
public class ThreadMain {


    public static void main(String[] args) throws InterruptedException {

       // 创建线程 B
       Thread threadB = new Thread(new Runnable() {
            @Override
            public void run() {
                // 启动线程 Thread-a
                ThreadA threadA = new ThreadA("Thread-a");
//                try {
//                    // 加入 join 方法
//                    threadA.getThread().join();
//                } catch (InterruptedException e) {
//                    e.printStackTrace();
//                }
                // 线程B的业务逻辑
                System.out.println("开始线程 " + Thread.currentThread().getName() + "业务逻辑");
                for (int i = 6; i > 0 ; i--) {
                    try {
                        System.out.println(Thread.currentThread().getName() + " : " + i);
                        // 睡眠0.5秒
                        Thread.sleep(500);
                    } catch (Exception e) {
                        System.out.println(Thread.currentThread().getName() + " 线程执行异常!");
                        e.printStackTrace();
                    }
                }

                System.out.println(Thread.currentThread().getName() + " 线程结束!");

            }
       }, "Thread-B");

       // 启动线程B
       threadB.start();
       // 加入 join 方法
//       threadB.join();

        // 主线程业务逻辑开始
        System.out.println("主线程业务逻辑开始");
        for (int i = 6; i > 0 ; i--) {
            try {
                System.out.println(Thread.currentThread().getName() + " : " + i);
                // 睡眠0.3秒
                Thread.sleep(300);
            } catch (Exception e) {
                System.out.println(Thread.currentThread().getName() + " 线程执行异常!");
                e.printStackTrace();
            }
        }

        System.out.println(Thread.currentThread().getName() + " 线程结束!");
    }
}

 
运行结果二:
在这里插入图片描述
 

2.3 对比说明

 

       测试代码一和测试代码二,唯一的差别就是:测试代码二将测试代码一中的,两次调用 join() 方法部分注释掉了。
 
       代码中,在线程 Thread-B 中调用了线程 Thread-a ,在主线程 Main 中调用了线程 Thread-B。同时,Thread-a
 
线程的睡眠时间是 1 秒,Thread-B 线程的睡眠时间是 0.5 秒,Main 线程的睡眠时间是 0.3 秒。
 
       当加入 join() 方法后:由于Thread-B 线程中调用 Thread-a,优先执行Thread-a 线程,当前正在执行的Thread-B
 
线程堵塞,直到Thread-a 线程执行完毕。同理,由于Main 主线程中调用 Thread-B线程,故优先执行Thread-B 线程,
 
当前正在执行的 Main 主线程堵塞,直到Thread-B 线程执行完毕。

总结:

        join() 方法的作用就是:将调用join的线程优先执行,当前正在执行的线程阻塞,直到调用join方法的线程执行完毕

或者被打断,主要用于线程之间的交互.

 

3、源码理解

 
深入源码了解一下join(),这里仅截取 Thread 类 的 join() 和 join(millis) 方法

package java.lang;

import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.security.AccessController;
import java.security.AccessControlContext;
import java.security.PrivilegedAction;
import java.util.Map;
import java.util.HashMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.locks.LockSupport;
import sun.nio.ch.Interruptible;
import sun.reflect.CallerSensitive;
import sun.reflect.Reflection;
import sun.security.util.SecurityConstants;

public
class Thread implements Runnable {
 
    ……
    ……   /** 这里仅截取 Thread 类 的 join() 和 join(millis) 方法 */
    ……

    /**
     * Waits for this thread to die.
     *
     * <p> An invocation of this method behaves in exactly the same
     * way as the invocation
     *
     * <blockquote>
     * {@linkplain #join(long) join}{@code (0)}
     * </blockquote>
     *
     * @throws  InterruptedException
     *          if any thread has interrupted the current thread. The
     *          <i>interrupted status</i> of the current thread is
     *          cleared when this exception is thrown.
     */
    public final void join() throws InterruptedException {
        join(0);
    }


    /**
     * Waits at most {@code millis} milliseconds for this thread to
     * die. A timeout of {@code 0} means to wait forever.
     *
     * <p> This implementation uses a loop of {@code this.wait} calls
     * conditioned on {@code this.isAlive}. As a thread terminates the
     * {@code this.notifyAll} method is invoked. It is recommended that
     * applications not use {@code wait}, {@code notify}, or
     * {@code notifyAll} on {@code Thread} instances.
     *
     * @param  millis
     *         the time to wait in milliseconds
     *
     * @throws  IllegalArgumentException
     *          if the value of {@code millis} is negative
     *
     * @throws  InterruptedException
     *          if any thread has interrupted the current thread. The
     *          <i>interrupted status</i> of the current thread is
     *          cleared when this exception is thrown.
     */
    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) {   // 无限期等待直到b线程结束
            while (isAlive()) { 
                // wait操作,那必然有synchronized与之对应,
                // 成员方法加了synchronized说明是synchronized(this)。
                // 注意这个wait()方法是Object类中的方法
                wait(0);   
            }
        } else {            // 等待固定时间,如果b没结束,那么就不等待了。
            while (isAlive()) {
                long delay = millis - now;
                if (delay <= 0) {
                    break;
                }
                wait(delay);
                now = System.currentTimeMillis() - base;
            }
        }
    }

 
 
 
 
线程模块,计划用一个系列博文整理总结。上一篇博文:  《线程系列1:创建线程
 
 
 
 
 
 
本文在源码理解部分,参考了博文: 【Java】Thread类中的join()方法原理. 非常感谢博主!!!

  • 5
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值