Java多线程-Thread.join()
Thread.join()把制定的线程加入到当前线程,可以将两个交替执行的多线程合并为顺序执行的线程。比如在线程B中调用累线程A的join()方法,直到线程A执行完毕后,才会继续执行线程B;
eg:
线程A代码:
public class A extends Thread{
@Override
public void run() {
for (int i =0; i <500 ; i++) {
System.out.println("A--thread:"+i);
}
}
}
线程B的代码:
public class Bextends Thread {
@Override
public void run() {
A a =new A();
a.start();
try {
a.join();// 调用join方法
for (int i =0; i <500 ; i++) {
System.out.println("Thread-B:"+i);
}
}catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args){
new B().start();
}
}
观察结果:在线程B的run方法中启动类线程A的start方法和join(),打印的结果没有交叉执行。可以得出上面的结论。感兴趣的同学可以将线程a.join()方法注视掉,观看打印结果有没有交叉执行。
结论: 当我们调用某个线程的join这个方法时,这个方法会挂起调用线程,直到被调用线程结束执行,调用线程才会继续执行。
源码解析:
Thread 源码有3个join方法重载。
public final void join()throws InterruptedException {
join(0);
};
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 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;
}
}
其中
a. join() 和 join(long millis, int nanos) 最后都调用了 join(long millis)。
b. 带参数的 join() 都是 synchronized method。
c. join() 调用了 join(0),从源码可以看到 join(0) 不断检查当前线程(join() 所属的线程实例,非调用线程)是否是 Active。
d. join() 和 sleep() 一样,都可以被中断(被中断时,会抛出 InterrupptedException 异常);不同的是,join() 内部调用了 wait(),会出让锁,而 sleep() 会一直保持锁。
以本文开头的代码为例,我们分析一下代码逻辑:
B 调用 a.join(),a.join() 再调用 a.join(0) (此时 B 会获得 child 实例作为锁,其他线程可以进入 child.join() ,但不可以进入 child.join(0)(同步的), 因为无法获取锁)。child.join(0) 会不断地检查 child 线程是否是 Active。
如果 child 线程是 Active,则循环调用 child.wait(0)(为了防止 Spurious wakeup, 需要将 wait(0) 放入 for 循环体中;此时 B 会释放 a 实例锁,其他线程可以竞争锁并进入 a.join(0)。我们可以得知,可以有多个线程等待某个线程执行完毕)。
一旦 a 线程不为 Active (状态为 TERMINATED), a.join(0) 会直接返回到 a.join(), a.join() 会直接返回到 B 父线程,B 父线程就可以继续运行下去了。