线程的方法之join()

人不敬我,是我无才,我不敬人,是我无德。
人不容我,是我无能,我不容人,是我无量。
人不助我,是我无为,我不助人,是我无善。

1. join()

Waits for this thread to die.(等待调用线程消亡…)

当我们在一个线程(宿主线程)上调用 join() 方法,则该线程(宿主线程)进入等待状态,直到调用join()的线程(调用线程)终止。

有很多朋友对Thread的join()理解不到位,原因就在于闹不清楚这里面的两个名词:

  1. 宿主线程
  2. 调用线程

下列代码:

class JoinThread extends Thread{
    public int processingCount = 0;
    // 宿主线程
    public Thread container;
    public JoinThread(int processingCount,String name,Thread container) {
        this.processingCount = processingCount;
        this.setName(name);
        this.container = container;
        System.out.println("Thread Created...");
    }

    @Override
    public void run() {
        System.out.println("Thread- "+this.getName()+" started...");
        while (processingCount > 0){
            try{
                System.out.println(container.getName()+" 宿主线程的状态:"+container.getState());
                System.out.println("调用线程的状态:"+this.getState());
                Thread.sleep(1000L);
                System.out.println("Current Value:"+processingCount);
            }catch (InterruptedException e){
                System.out.println("Thread- "+ this.getName() +" interrupted...");
            }
            processingCount --;
        }
        System.out.println("Thread "+ this.getName()+" exiting...");
    }
}

调用代码:

 public static void main(String[] args) throws InterruptedException {
        Thread t1 = new JoinThread(5,"Thread-A",Thread.currentThread());
        t1.start();
        System.out.println("Invoking join...");
        t1.join();
        System.out.println("Returned from join...");
        System.out.println(t1.isAlive());
    }

在这里插入图片描述

此处的宿主线程一直处于等待状态,直到调用线程结束。注意此处也需要处理异常:InterruptedException

如果调用线程阻塞或者需要花费太多时间去处理,则 join() 将会一直等待,这将会导致一个问题——调用线程无响应

2. join() 的同胞兄弟

  • join(long millis) : 为了等待调用线程消亡,最多 millis 毫秒(0 :意味着永远等待)
  • join(long mills, int nanos) :为了等待调用线程消亡,最多 millis + nanos 纳秒。

定时的 join() 方法 依赖于 OS的计时,因此我们不能精确地等待指定的时间。

 public static void main(String[] args) throws InterruptedException {
        Thread t1 = new JoinThread(5,"Thread-A",Thread.currentThread());
        t1.start();
        System.out.println("Invoking join...");
        // 注意这里
        t1.join(1000L);
        System.out.println("Returned from join...");
        System.out.println(t1.isAlive());
    }

结果如下:
在这里插入图片描述

3. join()的同步效果

除了(In addition to )等待调用线程消亡,调用 join()方法还有一个同步效果。join()创建了一种happens-before的关系。

3.1 What’s the happens-before关系

简而言之,如果在线程1中调用了线程2.join(),则线程2中的所有变化对于线程1都是可见的。反之,如果我们不去调用join()或者其它同步策略,我们不能保证在当前线程中能观察到其它线程的正确状态。
因此(Hence),即使调用join的线程能立即返回终止状态,我们有时仍然需要调用join()方法。

运行下面的程序,可以加深对此的理解

  static class MyThread extends  Thread{
        public  int count = 0;

        public MyThread(int count) {
            this.count = count;
        }

        @Override
        public void run() {
            while (count>0){
                try {
                    System.out.println("Current value:"+count);
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                count --;
            }
        }
    }
 public static void main(String[] args) throws InterruptedException {
        MyThread myThread = new MyThread(10);
        myThread.start();
        // 如果不调用join()相关方法, 即使myThread结束,也不能保证程序停止
        do{
            System.out.println("Running always...?");
            // myThread.join();
           // myThread.join(1000);
        }while (myThread.count >0);
    }

这里,主线程对MyThread的状态不敏感

4. join()的源码解析

通过前面的分析,我们可以看出join() 方法会引起宿主线程的阻塞,我们就来看一下:它是如何实现这一功能的。

4.1 join()源码
// 注意这里的“synchronized”关键字,加锁当前线程对象
/**
* @param : mills 等待时长(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); // 注意这里的wait()
            }
        } else {
            throw new IllegalArgumentException("timeout value is negative");
        }
    }

我们可以发现阻塞功能是由Object 的wait() 方法来实现的。调用wait()方法必须要获取锁,所以join()方法被synchronized关键字修饰。另外我们知道,wait()要和**notify()或者notifyAll()**一般都是配合使用,进行 唤醒操作。但是貌似没看到它的身影啊。

4.2 join()的wait()的唤醒操作在哪里?

我们可以先来看一下线程退出时,会做什么操作?

JDK底层的thread.cpp源码

  1. 线程退出的方法:exit()
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).
  ensure_join(this); 
  assert(!this->has_pending_exception(), "ensure_join should have cleared");
  
   ... 
}
  1. ensure_join()的实现

ensure_join()内部调用了lock.notify_all(thread)进行唤醒操作

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);
  lock.notify_all(thread);// 调用了notify_all(thread)
  // Ignore pending exception (ThreadDeath), since we are exiting anyway
  thread->clear_pending_exception();
}

我们会发现,当线程终止时,会调用线程自身的notifyAll()方法,通知所有等待该线程对象上的线程。

5. 关于join()的总结

  1. join()是Thread的方法,其方法被asynchronized关键字修饰。
  2. join()还是通过等待/通知机制来实现其逻辑的,本质上还是加锁——>循环——>处理这3个步骤。(wait,notifyAll)

参考文章:

  1. Thread.join的作用和原理
  2. The Thread.join() Method in Java
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值