@ 先看一个案例:小明和妈妈准备做饭,小明呢,负责买菜,妈妈负责做饭,做饭的前提呢是要等小明买完菜回来才能做饭,想一想如何实现呢?
以往常方式,我们是这样写的:
// 省略了try catch 捕获
new Thread(() -> {
Thread.sleep(6_000);
System.out.println("买完菜回来了!");
}, "小明").start();
new Thread(() -> {
Thread.sleep(5_000);
System.out.println("饭已做好!");
}, "妈妈").start();
复制代码
实际结果却是:
菜没买回来,饭却做好了,这是怎么回事呢,哦,原来是我们的程序是异步执行的,小明跟妈妈之间 没任何的关联,各执行各的,所以就造成了上面的错误。
那该如何解决呢,请看下面的解释……
我们要让小明执行完,妈妈才能执行,这里的过程称为同步,那么实现同步,方法就有很多了,但是这里我们不一一解释,我们使用 join
来实现此功能……
Thread xiaoMing = new Thread(() -> {
try {
Thread.sleep(4_000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("买完菜回来了!");
}, "小明");
xiaoMing.start();
xiaoMing.join();
try {
Thread.sleep(3_000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("妈妈饭已做好!");
复制代码
运行结果:
原理
join()
方法有两个重载方法,
1:join(long millis)
–> 表示在 millis 毫秒之后如果还没执行完,则主线程可以执行了。
2:join(long millis, int nanos)
–> 表示在 millis 毫秒 + nanos纳秒 之后如果还没执行完,则主线程可以执行了。
注:如果不传参数,默认参数为 0
join
源码
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) { // 此处判断当 join 的参数为0时,会调用 wait 方法进行无期限等待
while (isAlive()) {
wait(0);
}
} else { // 如果传递了参数,则不会无限期等待
while (isAlive()) {
long delay = millis - now;
if (delay <= 0) {
break;
}
wait(delay);
now = System.currentTimeMillis() - base;
}
}
}
复制代码
总结:首先 join()
是一个 synchronized
方法, 里面调用了 wait()
,这个过程的目的是让持有这个同步锁的线程进入等待,那么谁持有了这个同步锁呢?答案是主线程,因为主线程调用了threadA.join()
方法,相当于在 threadA.join()
代码这块写了一个同步代码块,谁去执行了这段代码呢,是主线程,所以主线程被wait()了。然后在子线程threadA执行完毕之后,JVM会调用lock.notify_all(thread);唤醒持有threadA这个对象锁的线程,也就是主线程,会继续执行。
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END