多线程之理解join()

很长一段时间都对join()方法有疑惑,主要有以下2点(为了表述清楚,把现在执行的线程称为main线程,在main中调用join()的线程称为sub线程)
(1)join()到底怎么实现main线程阻塞,sub线程执行的
(2)sub线程执行玩之后为什么main线程会被唤醒
为了解决这个问题,可以从这样一个例子出发

public static void main(String[] args){
        Thread t1=new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    Thread.sleep(300);
                }catch (Exception e){}
                System.out.println(Thread.currentThread().getId()+"-"+"sub线程");
            }
        });
        try{
            t1.start();
            t1.join();
        }catch (Exception e){}
        System.out.println("main线程执行");
    }

执行结果如下:

11-sub线程
11-sub线程
11-sub线程
11-sub线程
11-sub线程
main线程执行

出现这样的结果在预料之中,因为join()的作用就是让正在执行的main线程阻塞,先执行sub线程,之后再继续执行main线程。不过具体实现的细节值得讨论。

执行过程如下:
(1)在调用join之后进入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;
            }
        }
    }

可以看出join()方法实际上是一个synchronized修饰的同步方法,这个意味着执行这个方法的线程会获得该方法所属对象实例的锁;而执行这个方法的正是main线程,这个对象的实例实际上就是ti(也就是sub线程对应的对象实例),所以main线程获得了ti的对象锁之后开始执行。
(2)因为millis为0,所以以下部分被执行

if (millis == 0) {
            while (isAlive()) {
                wait(0);
            }
        }

isAlive()方法实际上就是this.isAlive(),也就是t1.isAlive(),因为ti.start()已经执行,t1.isAlive()为true,所以进入执行wait(0)。wait(0)方法的作用是阻塞当前执行的线程,也就是执行他的main线程(需要注意:虽然是t1.wait(0),但是阻塞的是main线程,并且main线程阻塞之后释放t1的对象锁然后进入t1对象的等待池中),sub线程开始执行。
(3)在sub线程执行完之后,退出时的实现如下

static void ensure_join(JavaThread* thread) {
  Handle threadObj(thread, thread->threadObj());
  assert(threadObj.not_null(), "Java thread object must exist");
  ObjectLocker lock(threadObj, thread);
  thread->clear_pending_exception();
  java_lang_Thread::set_stillborn(threadObj());
  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();
}

可以看出在线程执行完毕之后退出时,会执行notify_all(thread),也就是notify等待池中所有的线程,所以在sub执行完毕之后,main线程被唤醒,也就是从t1的等待池中出来进入t1的锁池(这里锁池中为空,所以main线程会直接执行)。main线程执行完毕之后程序执行完毕。

注意:如果millis不是0,那么那么执行wait(millis),表示在等待millis时间之后如果main线程会自动被唤醒

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值