龙空技术网

Java并发编程系列(三)——彻底理解thread.join()原理

爱学习的宅男 101

前言:

眼前大家对“java jion方法”大概比较关切,看官们都想要学习一些“java jion方法”的相关知识。那么小编在网摘上搜集了一些对于“java jion方法””的相关内容,希望兄弟们能喜欢,同学们快快来了解一下吧!

join() 方法的作用

在Java多线程中,没办法通过调配CPU执行时间,保证一个线程执行完成以后,另外一个再执行,要实现这个效果,可以通过join()方法来实现。

用线程的状态(详细的线程状态可以点击查看)来解释的话,A线程在执行B线程的join()或join(timeout)方法以后,A线程进入到WAITING或者TIMED_WAITING状态,一直等到B线程进入到TERMINATED状态或者A线程等待超时以后,A线程才会回到RUNNABLE状态。

简单的示例

废话不多说,先上个简单示例一起看一下:

public class SubThread extends Thread {    @Override    public void run() {        System.out.println("子线程开始执行");        try {            sleep(2000);        } catch (InterruptedException e) {            e.printStackTrace();        }        System.out.println("子线程执行完成");    }}
public class MainThread {    public static void main(String[] args) throws InterruptedException {        System.out.println("主线程开始执行");        SubThread thread = new SubThread();        thread.start();        thread.join();        System.out.println("主线程执行完成");    }}

打印结果:

主线程开始执行子线程开始执行子线程执行完成主线程执行完成

不管执行几次,打印结果都是一致的,说明主线程确实要比等待了2秒的子线程还要晚执行结束。

实现原理

现在功能也了解了,示例也看过了,是时候分析一下join方法的原理。

public final void join() throws InterruptedException {      join(0);}

join()方法内部调用了join(0)方法。

public final synchronized void join(long millis)throws InterruptedException {    long base = System.currentTimeMillis();    long now = 0;        // 如果传入参数小于0,则直接抛出异常    if (millis < 0) {        throw new IllegalArgumentException("timeout value is negative");    }    if (millis == 0) {        // 如果传入参数等于0,则无限等待        while (isAlive()) {            wait(0);        }    } else {        // 如果传入参数大于0,则会设置超时时间        while (isAlive()) {            long delay = millis - now;            if (delay <= 0) {                break;            }            wait(delay);            now = System.currentTimeMillis() - base;        }    }}

从源码中我们可以看到,A线程调用了B线程的join方法,首先判断了B线程是否还存活,就是isAlive()方法,这个方法是一个native方法,如果B线程还是存活的,则调用wait()方法(点击了解wait()方法的作用)。因为while的存在,即便A线程被唤醒,除非isAlive()为false或者等待超时,不然会继续调用wait()方法。

但是这个有另外一个问题,我们知道wait方法是配合notify使用的,但是我们上面的示例里面并没有调用B线程的notify方法,那个A线程是什么时候被唤醒的呢?其实啊,在每一个线程退出的时候,都会调用一下notifyAll这个方法,我们一起来看一下C++的源码:

oid JavaThread::exit(booldestory_vm, ExitTypeexit_type);static void ensure_join(JavaThread*thread) {	Handle threadObj(thread, thread -> threadObj());	ObjectLocker lock(threadObj, thread);	thread -> clear_pending_exception();    // 修改为 TERMINATED 状态	java_lang_Thread::set_thread_status(threadObj(), java_lang_Thread::TERMINATED);	java_lang_Thread::set_thread(threadObj(), NULL);    //执行了notifyAll()操作	lock.notify_all(thread);	thread -> clear_pending_exception();}
最后的最后

join()方法有一个会导致死锁的问题,就是执行Thread.currentThread().join(),执行这个代码以后,相当于线程等待自己结束以后再执行,那肯定是没办法实现的嘛。我们也看一个小示例验证一下:

public class MainThread {    public static void main(String[] args) throws InterruptedException {        System.out.println("主线程开始执行");        Thread.currentThread().join();        System.out.println("主线程执行完成");    }}

主线程在执行完Thread.currentThread().join();后,再也不会结束了。因此我们在开发的时候一定警惕死锁问题,类似的,A线程调用B.join(),但是B线程又调用A.join(),也是妥妥的死锁。

标签: #java jion方法