简述
我们在使用多线程时,有时候会有这样的需求:
- 主线程创建子线程并启动后,有可能子线程中存在比较耗时的操作(但耗时多少不确定),主线程往往会早于子线程结束,如果我们想要在子线程完成后再结束主线程呢?
- 首先想到的是Thread.sleep(xxx),但我们无法确定应该睡眠多少时间
- 而join()方法满足了我们的需求
源码
join()方法的作用是阻塞当前线程,直到调用join()的线程结束销毁,或者指定阻塞时长,若线程没停止但是超时,取消阻塞
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;
}
}
}
1、方法参数millis是指定join的时长(join()默认参数为0)
2、base是当join()方法开始执行的初始时间,now是方法运行的时长
3、当millis<0时,抛出非法参数异常
4、当millis=0时,当前线程无线等待,直到调用join()的线程停止销毁
5、当millis>0时,阻塞指定时长,为了避免被唤醒时间也不会出差错,
需要循环计算运行时间now,当now >= millis时,表示时间已到,退出循环。
【例如】
ThreadA a = new ThreadA();
a.start();//假设a会执行50s
a.join();
System.out.println("我要在a执行完后打印");
a.join()会无限阻塞当前线程,直到a执行完毕并销毁,达到目的效果
【异常】
在join()过程中,如果当前线程被打断,会抛出异常
//伪码
ThreadB:
run(){
ThreadA a = new ThreadA();
a.start();
a.join();
}
如果b线程在运行过程中被打断了,而a处于join状态,b会抛出InterruptedException异常,但是a不受影响
其他情况
上面我们了解到,join()能够做到安排线程执行顺序,但是这是一定的吗?或者说join后面的方法一定后执行吗?答案是否定的。
我们看到join方法的定义中有synchronized加持(因为内部使用的是wait()),因此有可能出现下面这种情况
- 有两个线程,线程A和线程B,A中含有成员变量b,并且将其作为一个锁对象
public class ThreadA extends Thread {
private ThreadB b;
public ThreadA(ThreadB b) {
super();
this.b = b;
}
@Override
public void run() {
try {
synchronized (b) {
System.out.println("begin A ThreadName="
+ Thread.currentThread().getName() + " "
+ System.currentTimeMillis());
Thread.sleep(5000);
System.out.println(" end A ThreadName="
+ Thread.currentThread().getName() + " "
+ System.currentTimeMillis());
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
- 线程B的run方法被synchronized加持,意味着锁对象为当前对象本身
public class ThreadB extends Thread {
@Override
synchronized public void run() {
try {
System.out.println("begin B ThreadName="
+ Thread.currentThread().getName() + " "
+ System.currentTimeMillis());
Thread.sleep(5000);
System.out.println(" end B ThreadName="
+ Thread.currentThread().getName() + " "
+ System.currentTimeMillis());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
- 运行如下代码
public static void main(String[] args) {
try {
ThreadB b = new ThreadB();
ThreadA a = new ThreadA(b);
a.start();
b.start();
b.join(2000);
System.out.println("我要比B后执行哦!");//1
} catch (Exception e) {
e.printStackTrace();
}
}
语句1是否能如愿在b之后打印呢?不一定!因为a线程,b线程,以及调用b.join(2000)的主线程,他们三个抢占的是同一把锁!有可能出现以下情况:
- b.join(2000)先抢占到锁,注意此时起始时间base已产生!然后wait(2000)释放锁
- a拿到锁,并执行完,花费了5s,此时b.join(2000)内部的wait(2000)也早执行完了,于是主线程和b线程又开始竞争锁!
- 很不幸主线程b.join(2000)又拿到了锁,但是和base一对比发现时间早过了,于是退出了循环
- 此时就剩b线程和语句1了,两者不存在竞争关系,所以打印顺序也可能出现多种,即主线程可能先于b线程打印语句1
网友评论