本文已参与「新人创作礼」活动,一起开启掘金创作之路。
使用场景
1. 主线程启动并创建子线程,如果子线程中要进行大量的耗时运算,主线程可能早于子线程结束。
2. 如果主线程需要知道子线程的执行结果时,就需要等待子线程执行结束。
3. 主线程可以使用sleep(xxx),但是这样等待的时间不好确定,因为子线程的执行时间不好确定。在这个场景中,join()就比较合适了。
主线程等待子线程的终止。也就是说,在主线程的代码块中,如果遇到了 Thread.join() 方法,此时主线程需要阻塞,等待子线程结束才能继续执行 Thread.join() 之后的代码块
详解
join() 方法是通过 wait() 实现的。
当主线程调用 join() 的时候,主线程会拿到子线程对象的锁,调用该对象的 wait() 方法,直到该对象唤醒主进程。
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");
}
// 超时时间等于0,意味着等待该线程结束没有时间限制
if (millis == 0) {
while (isAlive()) {
wait(0);
}
} else {
while (isAlive()) {
long delay = millis - now;
if (delay <= 0) {
break;
}
wait(delay);
now = System.currentTimeMillis() - base;
}
}
}
public final synchronized void join(long millis, int nanos)
throws InterruptedException {
if (millis < 0) {
throw new IllegalArgumentException("timeout value is negative");
}
if (nanos < 0 || nanos > 999999) {
throw new IllegalArgumentException(
"nanosecond timeout value out of range");
}
if (nanos >= 500000 || (nanos != 0 && millis == 0)) {
millis++;
}
join(millis);
}
public final void join() throws InterruptedException {
join(0);
}
从源码中可以看出
-
join()、join(0)是等价的,它们最终都调用了 Object.wait(0) 方法,而Object.wait(0) 方法是一直等待的,直到被 notify / 中断才会返回
-
join() 和 sleep() 一样都可以被中断,中断时会抛出 InterruptedException 异常。不同的是,join() 内部调用了 wait(),会让出锁,而 sleep() 会一直保持锁。
注意
- join() 方法必须在线程 start() 方法调用之后才有意义。
- 在 join() 过程中,如果当前线程被中断,则当前线程出现异常。A线程调用了B线程的join()方法,A线程中断会报异常而B线程不会中断。例:
public class InterruptTest {
public static void main(String[] args) throws Exception{
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
BThread bThread = new BThread();
bThread.start();
String name = Thread.currentThread().getName();
System.out.println(name + " 开始执行");
try {
Thread.sleep(2000);
Thread.currentThread().interrupt();
bThread.join();
} catch (InterruptedException e) {
System.out.println(name + " 中断");
}
System.out.println(name + " 结束");
}
},"[NThread] Thread");
thread.start();
thread.join();
}
}
public class BThread extends Thread{
public BThread(){
super("[BThread] Thread");
}
@Override
public void run(){
String threadName = Thread.currentThread().getName();
System.out.println(threadName + " 开始执行");
try {
for (int i = 0;i < 10;i++){
System.out.println(threadName + " loop at " + i);
Thread.sleep(1000);
}
System.out.println(threadName + " 结束" );
}catch (Exception e){
System.out.print(threadName);
e.printStackTrace();
}
}
}
执行结果
[NThread] Thread 开始执行
[BThread] Thread 开始执行
[BThread] Thread loop at 0
[BThread] Thread loop at 1
[BThread] Thread loop at 2
[NThread] Thread 中断
[NThread] Thread 结束
[BThread] Thread loop at 3
[BThread] Thread loop at 4
[BThread] Thread loop at 5
[BThread] Thread loop at 6
[BThread] Thread loop at 7
[BThread] Thread loop at 8
[BThread] Thread loop at 9
[BThread] Thread 结束