1.问题描述
当我们想要一个线程插队执行的时候,我们可能会使用到thread.join();。这个会让子线程先于主线程执行完毕,然后才开始执行子线程。但是仔细一想,发现这个明明调用的是子线程的join()方法,按道理应该子线程等待执行才是,为什么反而是主线程等待了呢?相关的示例代码如下:
代码语言:javascript复制public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(() -> {
try {
Thread.sleep(2000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("子线程开始执行...");
});
thread.start();
thread.join();
System.out.println("主线程执行...");
}
输出:
代码语言:javascript复制子线程开始执行...
主线程执行...
2.查看源码说明
带着这个主线程等待执行的疑惑来一起看下join的源码,如下所示:
代码语言:javascript复制/**
* Waits for this thread to die.
*
* <p> An invocation of this method behaves in exactly the same
* way as the invocation
*
* <blockquote>
* {@linkplain #join(long) join}{@code (0)}
* </blockquote>
*
* @throws InterruptedException
* if any thread has interrupted the current thread. The
* <i>interrupted status</i> of the current thread is
* cleared when this exception is thrown.
*/
public final void join() throws InterruptedException {
join(0);
}
谁等待谁终止?源码中注释说明的是等待这个线程终止,那就是等待调用Join()的线程终止,再继续往下看:
代码语言:javascript复制 /**
* Waits at most {@code millis} milliseconds for this thread to
* die. A timeout of {@code 0} means to wait forever.
*
* <p> This implementation uses a loop of {@code this.wait} calls
* conditioned on {@code this.isAlive}. As a thread terminates the
* {@code this.notifyAll} method is invoked. It is recommended that
* applications not use {@code wait}, {@code notify}, or
* {@code notifyAll} on {@code Thread} instances.
*
* @param millis
* the time to wait in milliseconds
*
* @throws IllegalArgumentException
* if the value of {@code millis} is negative
*
* @throws InterruptedException
* if any thread has interrupted the current thread. The
* <i>interrupted status</i> of the current thread is
* cleared when this exception is thrown.
*/
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;
}
}
}
从Join的源码中,我们可以看到它使用了while (isAlive()) 循环判断线程的存活状态,满足就调用wait方法,当有设置时长的时候会根据时长来进行等待。注意按照程序的执行顺序,我们这里是主线程调用的Thread的Join方法,所以是判断子线程的存活状态,满足则让子线程执行,主线程来等待。
wait 等待方法是让线程进入等待队列,使用方法是 obj.wait(); 这样当前线程就会暂停运行,并且进入obj的等待队列中,称作“线程正在obj上等待”。可以把子线程t理解为一个普通的obj对象,调用t的wait()方法,实际上就是主线程(main线程)在childThread对象的队列上等待,可以转换为如下写法进一步理解:
代码语言:javascript复制/**
* 主线程
*/
public class MainTest {
public static void main(String[] args) throws InterruptedException {
System.out.println("主线程开始执行...");
Thread childThread = new ChildThread();
childThread.start();
while (childThread.isAlive()) {
synchronized (childThread) {
// 此时主线程进行等待并释放锁资源 使子线程有得以运行的机会
childThread.wait();
}
}
System.out.println("主线程执行结束...");
}
}
/**
* 子线程
*/
class ChildThread extends Thread {
public ChildThread() {
}
@Override
public void run() {
System.out.println("子线程开始执行...");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("子线程执行结束...");
synchronized (this) {
// 唤醒等待的主线程
this.notifyAll();
}
}
}
输出:
代码语言:javascript复制主线程开始执行...
主线程执行结束...
子线程开始执行...
子线程执行结束...
另外需要强调的一点:使用Join方法让主线程等待后,调用完wait方法后,JVM底层会隐式的调用notifyAll方法来唤醒主线程,使其得以继续往下执行。