Thread.join jvm底层实现

目录


前言

探究Thread类join方法的使用及源码分析,并从jvm底层分析实现原理

一、Thread.join的作用是什么?

是线程之间通信的一种方式,具体来说就是让一个线程等待另一个线程执行完成

二、用法

使用join的情况:

class ThreadJoinTest {

    public static void main(String[] args) throws InterruptedException {
        Thread t =new Thread(()->{
            System.out.println("我是子线程");
            try {
                Thread.sleep(1000*3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        t.start();
        //使当前线程也就是执行t.join()的线程(主线程)等待线程t执行完成
        t.join();
        System.out.println("我是主线程");
    }
}

--- 运行结果
我是子线程
我是主线程

Process finished with exit code 0

不使用join的情况:

class ThreadJoinTest {

    public static void main(String[] args) throws InterruptedException {
        Thread t =new Thread(()->{
            System.out.println("我是子线程");
            try {
                Thread.sleep(1000*3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        t.start();
        System.out.println("我是主线程");
    }
}

---运行结果
我是主线程
我是子线程

Process finished with exit code 0

使用join后主线程会等待线程t执行完毕才会继续执行,不使用join是无法保证线程t的执行顺序的 

二、原理分析

  1.先看下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;
            }
        }
    }

 可以看到最终线程实现等待是调用了Object的wait方法,所以join方法加了synchronized ,wait是基于对象的Monitor实现的,objectMonitor是Monitor的c++实现,每个java对象都会关联一个objectMonitor下面我们看下objectMonitor的结构

ObjectMonitor() {
    // 多线程竞争锁进入时的单向链表
    ObjectWaiter * volatile _cxq;
    //处于等待锁block状态的线程,会被加入到该列表
    ObjectWaiter * volatile _EntryList;
    // _header是一个markOop类型,markOop就是对象头中的Mark Word
    volatile markOop _header;
    // 抢占该锁的线程数,约等于WaitSet.size + EntryList.size
    volatile intptr_t _count;
    // 等待线程数
  	volatile intptr_t _waiters;
    // 锁的重入次数
    volatile intptr_ _recursions;
    // 监视器锁寄生的对象,锁是寄托存储于对象中
    void* volatile  _object;
    // 指向持有ObjectMonitor对象的线程
    void* volatile _owner;
    // 处于wait状态的线程,会被加入到_WaitSet
    ObjectWaiter * volatile _WaitSet;
    // 操作WaitSet链表的锁
    volatile int _WaitSetLock;
    // 嵌套加锁次数,最外层锁的_recursions属性为0
    volatile intptr_t  _recursions;
}

objectMonitor维护了_WaitSet变量,这个变量存放的就是已经持有对象synchronized锁并执行wait方法的线程,用上面的示例代码解释就是主线程执行t.join时,先获取线程对象t的synchronized锁具体就是用cas把主线程对象放入ObjectMonitor._owner,执行wait方法后,主线程的线程对象会放入线程对象t关联的ObjectMonitor._waitSet,同时清空ObjectMonitor._owner完成锁的释放,从而实现主线程的等待。

2.从join方法可以看出执行wait方法后就结束了,那么主线程是怎么被唤醒的呢?

主线程被唤醒应该是t线程执行完之后发生的,具体怎么实现的就需要从jvm底层分析线程执行过程,线程是由jvm通过和操作系统交互最终由操作系统调度执行的。下面就是一个线程执行结束要执行的exit方法,它是jvm底层源码,由C++实现

//源码位置https://github.com/openjdk/jdk/blob/master/src/hotspot/share/runtime/javaThread.cpp
void JavaThread::exit(bool destroy_vm, ExitType exit_type) {
    // ...
 
    // Notify waiters on thread object. This has to be done after exit() is called
    // on the thread (if the thread is the last thread in a daemon ThreadGroup the
    // group should have the destroyed bit set before waiters are notified).
    // 根据源码注释可知大概就是唤醒等待在当前线程objectMonitor对象上waiters 
    ensure_join(this);
 
    // ...
}
 
 
static void ensure_join(JavaThread* thread) {
    // We do not need to grap the Threads_lock, since we are operating on ourself.
    Handle threadObj(thread, thread->threadObj());
    assert(threadObj.not_null(), "java thread object must exist");
    ObjectLocker lock(threadObj, thread);
    // Ignore pending exception (ThreadDeath), since we are exiting anyway
    thread->clear_pending_exception();
    // Thread is exiting. So set thread_status field in  java.lang.Thread class to TERMINATED.
    java_lang_Thread::set_thread_status(threadObj(), java_lang_Thread::TERMINATED);
    // Clear the native thread instance - this makes isAlive return false and allows the join()
    // to complete once we've done the notify_all below
    java_lang_Thread::set_thread(threadObj(), NULL);
 
    // 兄弟们这就是真相
    // thread就是当前线程就是代码示例中的t线程
    // 此处对应就是执行Object的notifyAll方法
    lock.notify_all(thread);
 
    // Ignore pending exception (ThreadDeath), since we are exiting anyway
    thread->clear_pending_exception();
}

至此join实现线程等待以及等待的线程怎么被唤醒的大致底层原理已经明确


总结

join是通过Object的wait实现等待,然后通过jvm和操作系统调度,最终调用notifyAll唤醒等待的线程来实现的

  • 7
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值