1. join()方法介绍
join()方法的主要作用是让“主线程”等待“子线程”结束之后才能继续运行,它可以使得线程之间的并行执行变为串行执行。在A线程中调用了B线程的join()方法时,表示只有当B线程执行完毕时,A线程才能继续执行。
join方法中如果传入参数,则表示这样的意思:如果A线程中掉用B线程的join(10),则表示A线程会等待B线程执行10毫秒,10毫秒过后,A、B线程并行执行。需要注意的是,jdk规定,join(0)的意思不是A线程等待B线程0秒,而是A线程等待B线程无限时间,直到B线程执行完毕,即join(0)等价于join()。(其实join()中调用的是join(0))
join方法必须在线程start方法调用之后调用才有意义。这个也很容易理解:如果一个线程都没有start,那它也就无法同步了。
2. join()方法示例
通过代码来演示join()方法:
public class JoinTest {
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 5; i++){
System.out.println("子线程运行" + i);
}
}
});
thread.start();
thread.join(); //使主线程等待子线程执行完毕
for (int i = 0; i < 5; i++){
System.out.println("主线程运行" + i);
}
}
}
运行结果:
此时如果我们将thread.join()这行代码注释掉,运行结果就会变成:
可以看出join()方法使子线程“加入”到了“主线程”中,主线程要等到子线程完成后才会继续执行。
3. join()方法源码解析
从上面的示例可以看出,join方法和wait()方法有些类似,wait()方法也是使线程进入阻塞状态,等待其他线程调用notify()方法唤醒。而join()方法不需要其他线程主动唤醒,当调用线程执行完毕后,它又会自己自动运行。实际上,join()方法内部也是调用了wait()方法来实现的,来看一下源码:
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;
}
}
}
在源码当中可以看出join()方法实际是调用join(long millis)方法,join(long millis)方法中我们是通过isAlive()方法来判断该线程是否还存活的。如果该线程还存活,则进行wait()操作,注意,这里调用wait()方法的是当前线程,当前线程是谁呢?正是主线程!。
那这里又有问题出现了,wait()方法必须出现在同步代码块中,而这里是拿了谁的锁呢?最后又是谁调用了notify()方法唤醒该线程呢?
从源码中可以看出synchronize加在了join()方法上,所以synchronize应该是锁住了当前的主线程,锁住该线程的,正是子线程。当子线程执行完成后,会执行notify()方法唤醒主线程,这时主线程就会继续执行!