本文已参与「新人创作礼」活动,一起开启掘金创作之路。
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