多线程基础(三) -- join方法详解,join阻塞的到底是什么线程
理解join()方法之前请确保对wait()/notify()/notifyAll()机制已熟练掌握。
join()方法的作用是等待线程销毁。
join()方法反应的是一个很现实的问题,比如main线程的执行时间是1s,子线程的执行时间是10s,但是主线程依赖子线程执行完的结果,这时怎么办?可以像生产者/消费者模型一样,搞一个缓冲区,子线程执行完把数据放在缓冲区中,通知main线程,main线程去拿,这样就不会浪费main线程的时间了。另外一种方法,就是join()了。
例一:阻塞mian线程
public class MyThread36 extends Thread {
public void run() {
try {
int secondValue = (int)(Math.random() * 10000);
System.out.println(secondValue);
Thread.sleep(secondValue);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) throws Exception {
MyThread36 mt = new MyThread36();
mt.start();
mt.join();
System.out.println("我想当mt对象执行完毕之后我再执行,我做到了");
}
看一下运行结果:
3111
我想当mt对象执行完毕之后我再执行,我做到了
意思是,join()方法会使调用join()方法的线程(也就是mt线程)所在的线程(也就是main线程)无限阻塞,直到调用join()方法的线程销毁为止,此例中main线程就会无限期阻塞直到mt的run()方法执行完毕。
例二:不能阻塞同级线程
如果大家对于上述的例子还有疑问:
比如有没有可能是2个并行线程,执行join方法阻塞另一个?
看一个例子:
public class TestJoin implements Runnable{
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("vip来了" + i + Thread.currentThread().getName());
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) throws InterruptedException {
TestJoin testJoin = new TestJoin();
Thread a = new Thread(testJoin, "a");
Thread b = new Thread(testJoin, "b");
a.start();
b.start();
a.join();
System.out.println("我是main,我完了!!!");
}
}
结果:
vip来了0b
vip来了0a
vip来了1b
vip来了1a
vip来了2b
vip来了2a
vip来了3b
vip来了3a
vip来了4a
vip来了4b
我是main,我完了!!!
如果按上述想法,很明显,应该等a执行完,再执行b,很明显结果并不是这样。
在看一个例子,证明是调用方也就是父线程被阻塞:
例三:阻塞调用方线程
public class TestJoin2 {
public static void main(String[] args) {
String threadName = Thread.currentThread().getName();
System.out.println(threadName + " start.");
BThread bt = new BThread();
AThread at = new AThread(bt);
try {
bt.start();
Thread.sleep(2000);
at.start();
at.join();
} catch (Exception e) {
System.out.println("Exception from main");
}
System.out.println(threadName + " end!");
}
}
class BThread extends Thread {
public BThread() {
super("[BThread] Thread");
};
public void run() {
String threadName = Thread.currentThread().getName();
System.out.println(threadName + " start.");
try {
for (int i = 0; i < 5; i++) {
System.out.println(threadName + " loop at " + i);
Thread.sleep(1000);
}
System.out.println(threadName + " end.");
} catch (Exception e) {
System.out.println("Exception from " + threadName + ".run");
}
}
}
class AThread extends Thread {
BThread bt;
public AThread(BThread bt) {
super("[AThread] Thread");
this.bt = bt;
}
public void run() {
String threadName = Thread.currentThread().getName();
System.out.println(threadName + " start.");
try {
bt.join();
System.out.println(threadName + " end.");
} catch (Exception e) {
System.out.println("Exception from " + threadName + ".run");
}
}
}
打印结果:
main start.
[BThread] Thread start. // 父线程B启动执行
[BThread] Thread loop at 0
[BThread] Thread loop at 1
[AThread] Thread start. // 2秒后main线程睡眠结束,启动at线程,at线程被bt.join阻塞
[BThread] Thread loop at 2
[BThread] Thread loop at 3
[BThread] Thread loop at 4
[BThread] Thread end.
[AThread] Thread end. // bt线程执行完,at线程开始执行
main end! // 由于调用了at.join,所以只能等待at线程执行完再执行at的父线程main线程
做一些修改,将上述的main方法中的at.join()注释,查看运行结果:
main start.
[BThread] Thread start. // bt线程启动
[BThread] Thread loop at 0
[BThread] Thread loop at 1
main end! // main线程由于没有被阻塞,所以和其他线程并行执行。此时main线程已经执行结束
[AThread] Thread start. // at线程启动,由于被bt.join阻塞,只能等bt线程执行完执行
[BThread] Thread loop at 2
[BThread] Thread loop at 3
[BThread] Thread loop at 4
[BThread] Thread end.
[AThread] Thread end.// at线程执行,
从这个例子可以看出来,阻塞的是调用join方法的线程的调用线程。
和sleep的区别:
join()方法的一个重点是要区分出和sleep()方法的区别。join(2000)也是可以的,表示调用join()方法的线程所在的线程最多等待2000ms,也就是比如main线程只会被阻塞2000毫秒。
两者的区别在于:sleep(2000)不释放锁,join(2000)释放锁,因为join()方法内部使用的是wait(),因此会释放锁。看一下join(2000)的源码就知道了,join()其实和join(2000)一样,无非是join(0)而已:
public final synchronized void join(final long millis) throws InterruptedException {
if (millis > 0) {
if (isAlive()) {
final long startTime = System.nanoTime();
long delay = millis;
do {
wait(delay);
} while (isAlive() && (delay = millis -
TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime)) > 0);
}
} else if (millis == 0) {
while (isAlive()) {
wait(0);
}
} else {
throw new IllegalArgumentException("timeout value is negative");
}
}
join与异常
如果join过程中,当前线程对象被中断,则当亲啊线程出现异常
参考:https://blog.csdn.net/sinat_29384657/article/details/52228578
https://www.cnblogs.com/xrq730/p/4851233.html
本文地址:https://blog.csdn.net/weixin_39724194/article/details/107362564
上一篇: 终端I/O函数 ioctl
下一篇: Linux环境搭建Web服务器