开发中常常会遇见这样的问题,主线程在运行的时候,需要一些子线程去进行相应的运算,如果主线程需要子线程提供一个处理后的数据,那么主线程就必须要等子线程运行完后才能继续执行。先看下面的这种情况
影视作品的一个拍摄场景中,这次不是主角(主线程)的独角戏,他需要一个配角(子线程)来搭戏,但是配角还没来,主角只能等配角来了才能开始,要是配角没来,主角非要开始的话那这个场景的戏就乱了。
public class SupportRule extends Thread{
@Override
public void run(){
try{
System.out.println("配角赶路中……");
Thread.sleep(5000);
System.out.println("配角来啦!!!马上化妆");
Thread.sleep(5000);
} catch (InterruptedException e){
e.printStackTrace();
}
}
}
public class JoinTest {
public static void main(String[] args){
SupportRule supportRule = new SupportRule();
supportRule.start();
System.out.println("我是主角先拍我!!!");
System.out.println("今天要有一个配角给你配戏才行啊,我看看配角到了没!");
System.out.println("我不管,我现在就要开拍!!!");
System.out.println("开拍……");
}
}
运行结果,额,这还让导游怎么拍。
这时就需要使用join()方法(当然,也可以使用强大的并发工具包)
join()方法定义在Thread类中,JDK中给出的定义就是等待该线程终止,那么可以在子线程中使用join()方法,实现主线程等待子线程执行完后在执行主线程的效果。
现在通过代码看下
public class JoinTest {
public static void main(String[] args){
try{
SupportRule supportRule = new SupportRule();
supportRule.start();
supportRule.join();
System.out.println("我是主角先拍我!!!");
System.out.println("今天要有一个配角给你配戏才行啊,我看看配角到了没!");
System.out.println("我不管,我现在就要开拍!!!");
System.out.println("开拍……");
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
嗯,这次导演很满意。继续深入看看join()方法的实现
反编译,有参的话等待相应的milliseconds后该线程死亡,无参的话(millis=0)则一直等待(直到该线程执行完成)。
/**
* 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) { // join()方法不传参数时,millis为0进入该分支
while (isAlive()) {
wait(0);
}
} else {
while (isAlive()) {
long delay = millis - now;
if (delay <= 0) {
break;
}
wait(delay);
now = System.currentTimeMillis() - base;
}
}
}
现在来看看这部分代码
while (isAlive()) { // 判断线程是否处于活动状态,也就是如果配角线程还活着,主角线程就要等,我去,到底谁是主角!!!
wait(0);
}
而且join方法是final的synchronized方法,那就说明配角线程要在主角线程里面搞事情啊,那没办法了,你搞就搞吧,记得最后唤醒我就行了,我妥协还不行。但是在join()方法里面并没有任何唤醒操作啊,继续挖,挖到JVM源码
任何Java线程在执行完后都会执行一个C++函数
void JavaThread::exit(bool destroy_vm, ExitType exit_type)
里面调用了一个ensure_join(this);
然后注意这个方法里面的notifyAll()
static void ensure_join(JavaThread* thread) {
Handle threadObj(thread, thread->threadObj());
ObjectLocker lock(threadObj, thread);
thread->clear_pending_exception();
java_lang_Thread::set_thread_status(threadObj(), java_lang_Thread::TERMINATED);
java_lang_Thread::set_thread(threadObj(), NULL);
lock.notify_all(thread);
thread->clear_pending_exception();
}
所以,join()方法的实现原理是使用wait()方法实现的,它达到的效果其实和使用synchronized类似,区别就是synchronized使用的是对象监听器。
join()方法还可传参,只等待指定的时间
public class JoinTest {
public static void main(String[] args){
try{
SupportRule supportRule = new SupportRule();
supportRule.start();
supportRule.join(4000);
System.out.println("我是主角先拍我!!!");
System.out.println("今天要有一个配角给你配戏才行啊,我看看配角到了没!");
System.out.println("我不管,我现在就要开拍!!!");
System.out.println("开拍……");
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
等待4秒回,主角开拍