【Java深入学习】多线程之join方法

114 阅读3分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

join有什么用

join的目的是为了把调用join的线程“插队”到了当前线程,并且 调用join的线程一定会把此线程运行结束。

扩展两个概念

同步:需要等待结果返回,才能继续运行就是同步

异步:不需要等待结果返回,就能继续运行就是异步

join怎么用

分析这段代码,说明r的值是多少

import lombok.extern.slf4j.Slf4j;
@Slf4j(topic = "c.Test10")
public class Test10 {
    static int r = 0;
    public static void main(String[] args) throws InterruptedException {
        test1();
    }
    private static void test1() throws InterruptedException {
        log.debug("开始");
        Thread t1 = new Thread(() -> {
            log.debug("开始");
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            log.debug("结束");
            r = 10;
        },"t1");
        t1.start();
        log.debug("结果为:{}", r);
        log.debug("结束");
    }
}

结果:

  • 14:50:09.053 c.Test10 [main] - 开始
  • 14:50:09.096 c.Test10 [t1] - 开始
  • 14:50:09.095 c.Test10 [main] - 结果为:0
  • 14:50:09.097 c.Test10 [main] - 结束
  • 14:50:09.098 c.Test10 [t1] - 结束

解释:

因为此时为异步,且t1线程在给r赋值前 sleep了1秒,导致主线程先运行了log.debug(“结果为:{}”, r)这个语句 此时这个r还没有被赋值故结果为0

代码进行修改后加入join

import lombok.extern.slf4j.Slf4j;
@Slf4j(topic = "c.Test10")
public class Test10 {
    static int r = 0;
    public static void main(String[] args) throws InterruptedException {
        test1();
    }
    private static void test1() throws InterruptedException {
        log.debug("开始");
        Thread t1 = new Thread(() -> {
            log.debug("开始");
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            log.debug("结束");
            r = 10;
        },"t1");
        t1.start();
        t1.join();
        log.debug("结果为:{}", r);
        log.debug("结束");
    }
}

结果:

  • 14:52:21.241 c.Test10 [main] - 开始
  • 14:52:21.277 c.Test10 [t1] - 开始
  • 14:52:21.279 c.Test10 [t1] - 结束
  • 14:52:21.279 c.Test10 [main] - 结果为:10
  • 14:52:21.280 c.Test10 [main] - 结束

解释:

因为此时是同步,t1.join()后,把t1线程加入到了主线程,并且把t1执行完成后才会继续执行主线程,所以此时r已经赋值,所以此时r=10

情况案例

分析下面代码花费的时间

import lombok.extern.slf4j.Slf4j;

@Slf4j(topic = "c.TestJoin")
public class TestJoin {
    static int r = 0;
    static int r1 = 0;
    static int r2 = 0;

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

    private static void test() throws InterruptedException {
        Thread t1 = new Thread(() -> {
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            r1 = 10;
        });
        Thread t2 = new Thread(() -> {
            try {
                Thread.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            r2 = 20;
    
        });
        t1.start();
        t2.start();
        long start = System.currentTimeMillis();
        log.debug("join begin");
        t1.join();
        log.debug("t1 join end");
        t2.join();
        log.debug("t2 join end");
        long end = System.currentTimeMillis();
        log.debug("r1: {} r2: {} cost: {}", r1, r2, end - start);
    }
}

结果:

  • 15:23:18.025 c.TestJoin [main] - join begin
  • 15:23:19.025 c.TestJoin [main] - t1 join end
  • 15:23:20.025 c.TestJoin [main] - t2 join end
  • 15:23:20.025 c.TestJoin [main] - r1: 10 r2: 20 cost: 2002

解释:

因为t1线程先同步到主线程 但是t1线程sleep了1s 在这个过程中 程序在t2线程运行1s,之后继续运行t1线程,t1线程结束后 程序运行t2线程 t2线程同步到主线程 因为t2线程之前已经运行过1s 又因为t2线程sleep 2s 所以还需要1s,故总共需要时间为2s

如果把t1 和 t2互换结果会改变吗

分析:

结果是不会变的 还是2s ,因为 t2线程先同步到了主线程 t2线程sleep了2s 这期间 运行了t1线程 因为t1线程只是sleep了1s 故 t1线程运行结束 当2s后 切换到t1线程 t1线程运行结束,故总共还是2s