Java并发05:Thread的基本方法(2)-join方法-线程插队

本章主要对Java中Thread类的基本方法进行学习。

1.序言

Thread类作为线程的基类,提供了一系列方法,主要有:

  • Thread.sleep(long):强制线程睡眠一段时间。
  • Thread.activeCount():获取当前程序中存活的线程数。
  • thread.start():启动一个线程。
  • Thread.currentThread():获取当前正在运行的线程。
  • thread.getThreadGroup():获取线程所在线程组。
  • thread.getName():获取线程的名字。
  • thread.getPriority():获取线程的优先级。
  • thread.setName(name):设置线程的名字。
  • thread.setPriority(priority):设置线程的优先级。
  • thread.isAlive():判断线程是否还存活着。
  • thread.isDaemon():判断线程是否是守护线程。
  • thread.setDaemon(true):将指定线程设置为守护线程。
  • thread.join():在当前线程中加入指定线程,使得这个指定线程等待当前线程,并在当前线程结束前结束。
  • thread.yield():使得当前线程退让出CPU资源,把CPU调度机会分配给同样线程优先级的线程。
  • thread.interrupt():使得指定线程中断阻塞状态,并将阻塞标志位置为true。
  • object.wai()、object.notify()、object.notifyAll():Object类提供的线程等待和线程唤醒方法。

为了便于阅读,将以上所有方法,放在5篇文章中进行学习。

本章主要学习绿色字体标记的方法,其他方法请参加其他章节。

2.join()方法的理解

先来看join()方法的定义与注释。

/**
 * 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);
}

join()方法的作用:Waits for this thread to die.即:等待这个线程结束。

举例说明:

  • 程序中有两个线程:[线程A] = threadA、[线程B]=threadB
  • 在[线程A]的run()方法中,调用了threadB.join()
  • 则[线程A]会在此段代码出,暂停后续代码的调用,等待[线程B]执行结束。
  • [线程B]执行结束之后,[线程A]才会继续执行后续的未完代码。

关于join()方法更形象的说法:线程插队

join()方法常用来保证:确保能够完整的获取到插队线程的处理结果。

3.实例代码与结果

3.1.示例场景

  • 程序中有两个线程1和2,线程1独立运行需要100毫秒,线程2独立运行需要200毫秒。
  • 线程2会对共享变量config进行赋值。
  • 线程1会读取共享变量config。
  • 我们期望线程1能够读取到被线程2赋值之后的变量config。

3.2.不用join()

代码:

/**
 * <p>java线程基本方法:join</p>
 *
 * @author hanchao 2018/3/11 16:03
 **/
public class ThreadJoinDemo {
    private static final Logger LOGGER = Logger.getLogger(ThreadJoinDemo.class);

    //线程间的共享资源
    private static String config = "配置未被初始化.";
    /**
     * <p>join:让当前线程等待指定的线程结束</p>
     *
     * @author hanchao 2018/3/11 16:04
     **/
    public static void main(String[] args) throws InterruptedException {
        //不加join
        LOGGER.info("==========不使用t.join()方法==========");
        new Thread(() -> {
            LOGGER.info("[线程1]开始运行...");
            Thread otherThread = new Thread(() -> {
                LOGGER.info("[线程2]开始运行...");
                try {
                    Thread.sleep(200);//线程2运行200毫秒
                    config = "配置已被[线程2]初始化.";
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                LOGGER.info("[线程2]运行结束");
            });
            //启动线程2
            otherThread.start();
            try {
                Thread.sleep(100);//线程1运行100毫秒
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            LOGGER.info("[线程1]获取的配置为:" + config);
            LOGGER.info("[线程1]运行结束");
        }).start();//启动线程1
    }
}

结果:

2018-03-12 12:40:24 INFO  ThreadJoinDemo:22 - ==========不使用t.join()方法==========
2018-03-12 12:40:24 INFO  ThreadJoinDemo:24 - [线程1]开始运行...
2018-03-12 12:40:24 INFO  ThreadJoinDemo:26 - [线程2]开始运行...
2018-03-12 12:40:24 INFO  ThreadJoinDemo:42 - [线程1]获取的配置为:配置未被初始化.
2018-03-12 12:40:24 INFO  ThreadJoinDemo:43 - [线程1]运行结束
2018-03-12 12:40:24 INFO  ThreadJoinDemo:33 - [线程2]运行结束

分析:

  • 线程1执行时间比线程2更短,所以导致:线程1执行完了,线程2还没来得及对配置config进行赋值。这不是我们想要的结果。

3.3.添加join()

代码:

/**
 * <p>java线程基本方法:join</p>
 *
 * @author hanchao 2018/3/11 16:03
 **/
public class ThreadJoinDemo {
    private static final Logger LOGGER = Logger.getLogger(ThreadJoinDemo.class);

    //线程间的共享资源
    private static String config = "配置未被初始化.";
    /**
     * <p>join:让当前线程等待指定的线程结束</p>
     *
     * @author hanchao 2018/3/11 16:04
     **/
    public static void main(String[] args) throws InterruptedException {
            //使用t.join()方法
        Thread.sleep(1000);//等待线程1和线程2都有运行完
        System.out.println();
        LOGGER.info("==========使用t.join()方法==========");
        new Thread(() -> {
            LOGGER.info("[线程11]开始运行...");
            Thread otherThread = new Thread(() -> {
                LOGGER.info("[线程22]开始运行...");
                try {
                    Thread.sleep(200);//线程22运行200毫秒
                    config = "配置已被[线程22]初始化.";
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                LOGGER.info("[线程22]运行结束");
            });
            otherThread.start();//启动线程22
            try {
                //在线程11中,加入线程22-->需要等到线程22结束之后才能结束线程11
                otherThread.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            LOGGER.info("[线程11]等待[线程22]结束");
            try {
                Thread.sleep(100);//线程11运行100毫秒
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            LOGGER.info("[线程1]获取的配置为:" + config);
            LOGGER.info("[线程11]运行结束");
        }).start();//启动线程11
    }
}

结果:

2018-03-12 12:40:25 INFO  ThreadJoinDemo:49 - ==========使用t.join()方法==========
2018-03-12 12:40:25 INFO  ThreadJoinDemo:51 - [线程11]开始运行...
2018-03-12 12:40:25 INFO  ThreadJoinDemo:53 - [线程22]开始运行...
2018-03-12 12:40:25 INFO  ThreadJoinDemo:60 - [线程22]运行结束
2018-03-12 12:40:25 INFO  ThreadJoinDemo:69 - [线程11]等待[线程22]结束
2018-03-12 12:40:25 INFO  ThreadJoinDemo:75 - [线程1]获取的配置为:配置已被[线程22]初始化.
2018-03-12 12:40:25 INFO  ThreadJoinDemo:76 - [线程11]运行结束

分析:

  • 在线程1的执行过程中,线程2通过join()加入。
  • 这时,线程1不会再继续执行后续代码,而是等待线程2执行结束。
  • 当线程2执行结束后,线程1继续执行未执行完的代码。
  • 通过join()方法,达到了预期效果。

 

posted @ 2020-10-27 17:40  姚春辉  阅读(216)  评论(0编辑  收藏  举报