JUC源码讲解:逐步解析 join()
问题抛出
join() 在源码中其实是使用了 wait() 方法的, 因此, wait() 的特性 join() 都能继承过来, 我们知道wait()有什么特性呢?
wait()的特性:
- 会释放锁
- 对中断异常敏感
- 会释放CPU时间片
我也对wait()做了讲解,想了解可移步 https://www.cnblogs.com/acdongla/p/18071381
可是, join() 真的会释放锁吗? 为什么一些例子中, 使用join() 后其他线程仍然被阻塞住了?join() 还有什么需要注意的特性吗? 让我们带着问题寻找答案吧
解析源码
进入 join() 看到这样一段代码
public final void join() throws InterruptedException {
join(0);
}
深入进去,点开看看:
这里先把源码贴出来,之后会逐步解析
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) {
while (isAlive()) {
wait(0);
}
} else {
while (isAlive()) {
long delay = millis - now;
if (delay <= 0) {
break;
}
wait(delay);
now = System.currentTimeMillis() - base;
}
}
}
先看这段代码:
if (millis < 0) {
throw new IllegalArgumentException("timeout value is negative");
}
可以看到, 传递的时间不能是负数
一个重点来了, alive() 函数出现了:
if (millis == 0) {
while (isAlive()) {
wait(0);
}
isAlive() 是对线程状态的检查, 必须是start()后的线程才会满足条件.
因此,因为有 isAlive() 的检查 , join()函数只能在线程 start() 后使用
注意!!! 这里的 wait(0)
我们知道, wait()能释放锁资源, 可我们需要注意一个问题, 这里的 wait() 释放的是谁的锁资源?
答: **释放的是当前线程的锁资源, 这里的wait()是 currentThread.wait(), 是 this.wait() **
我们写一段代码来说明这个问题
public class Main {
@SneakyThrows
public static void main(String[] args) {
new Thread(new YYThread(), "YY-1").start();
Thread.sleep(100);
new Thread(new YYThread(), "YY-2").start();
}
static class YYThread implements Runnable {
@SneakyThrows
@Override
public void run() {
/// 注意!!! 这里使用锁对象是 Main.class
synchronized (Main.clsss) {
System.out.println(Thread.currentThread().getName() + "启动了");
Thread.currentThread().join();
}
}
}
}
注意, 在sync中使用的锁对象是 Main.class
运行这段代码,会发现线程 "YY-2" 被阻塞了
为什么, join()不是同wait()一样,会释放锁资源吗?
但!!! wait()释放的是当前线程的锁资源, 因此,使用全局变量来作为锁是不可行的
我们把run()函数做这样的修改,就可以不阻塞了
public void run() {
/// 修改了锁, 修改为了当前线程
synchronized (Thread.currentThread()) {
System.out.println(Thread.currentThread().getName() + "启动了");
Thread.currentThread().join();
}
}
修改后,从输出上看,两个线程都正常运行了
总结
- join() 完全继承了 wait() 方法的特性, 但需要注意的事, join() 中的源码使用了wait(), 代表的事 currentThread.wait(), 是"当前线程.wait()",因此释放的只是当前线程的锁资源
- 因为源码中对isAlive() 的判断, join() 只能在线程 start() 之后才能使用
- join() 的参数不能是负数