zoukankan      html  css  js  c++  java
  • 由Thread.join引发的思考

    下面是一段司空见惯的代码,创建两个线程A和线程B,使得线程A优先于线程B执行,使得线程B优先于主线程执行

    public class Demo52 {
        public static void main(String[] args) {
             
            Thread thread1 = new Thread(()->{
                System.out.println("线程:"+Thread.currentThread().getName());
            },"A");
            Thread thread2 = new Thread(()->{
                try {
                    thread1.join();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("线程:"+Thread.currentThread().getName());
            },"B");
            thread1.start();
            thread2.start();
            try {
                thread2.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("main线程");
        }
    }
    

    输出结果:

    线程:A
    线程:B
    main线程
    

    它是如何做到的线程A优先于线程B,线程B优先于主线程的呢?

    为了说明这点,就要查看Thread.join的源码了:

         /** 等待该线程终止
         * Waits for this thread to die.
         * 调用此方法的行为方式与调用完全相同join (0)
         * <p> An invocation of this method behaves in exactly the same
         * way as the invocation
         *
         * <blockquote>
         * {@linkplain #join(long) join}{@code (0)}
         * </blockquote>
         *
         * @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 void join() throws InterruptedException {
            join(0);
    }
    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;
                }
            }
    }
    public final native void wait(long timeout) throws InterruptedException;
    

    ​ 结合上面的实例,先看主线程和线程B,在主线程中执行“thread2.join();”,也就相当进入到join方法体中,它会获取到当前实例的锁,也就是线程B对象的锁,然后判断线程是否存活后执行“ wait(0);”,执行该方法时,主线程会释放锁,进入到阻塞状态(也即进入到了该锁的WaitSet中)。为什么说是主线程进入到阻塞状态,而不是线程B进入到阻塞状态呢?

    为了解答这个问题,设计如下的实例:

    class ThreadTest extends Thread{
        public synchronized void method1(){
            System.out.println("hello world");
            System.out.println(Thread.currentThread().getName());
        }
    }
    public class Demo51 {
        public static void main(String[] args) {
            ThreadTest threadTest = new ThreadTest();
            threadTest.start();
            threadTest.method1();
        }
    }
    

    输出结果:

    hello world
    main
    

    ​ 可以看到在执行“threadTest.method1()”时,线程“threadTest”中输出的当前线程是主线程,而不是“Thread-0”,这是因为它是“ threadTest.method1()”是由主线程所调用的。

    ​ 再回到上面的问题中,调用“thread2.join();”导致主线程被阻塞,紧接着线程B开始执行,线程B执行完毕后开始执行主线程,而此时主线程还在阻塞状态(即还在该线程锁的waitset中),那么它是如何实现唤醒主线程,使得它能够接着执行的呢?实际上这是因为每个线程在退出时,会执行notifyAll唤醒所有阻塞在该实例锁上的线程。

    为了更为方便的说明这个问题,设计如下的实例,创建一个线程类,定义“method1”方法和“run”方法,然后在主线程中调用它

    class ThreadTest extends Thread{
    
        public synchronized void method1() throws InterruptedException {
            System.out.println("hello world");
            System.out.println(Thread.currentThread().getName());
            wait();
            System.out.println("==================");
        }
    
        @Override
        public void run() {
            try {
                Thread.sleep(10_000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    public class Demo51 {
        public static void main(String[] args) throws InterruptedException {
            ThreadTest threadTest = new ThreadTest();
            threadTest.start();
            threadTest.method1();
            System.out.println("===Exit===");
        }
    }
    

    运行结果:

    hello world
    main
    ==================
    ===Exit===
    

    ​ 来分析一下程序的执行,“ThreadTest threadTest = new ThreadTest()”创建线程对象,“threadTest.start();”启动这个线程,“threadTest.method1();”执行method1方法,关注点就在这里,在执行method1方法的时候,主线程会尝试获取“threadTest ”对象的锁,成功后进入到方法体,然后进入到阻塞状态,而threadTest 在启动后执行run方法中的内容,然后睡眠10秒钟,在随眠结束后执行完毕,threadTest 退出执行状态,在退出时,执行notify_all方法唤醒阻塞在threadTest 锁上的主线程。

    // 位于/hotspot/src/share/vm/runtime/thread.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).
    // 有一个贼不起眼的一行代码,就是这行
    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就是当前线程,是啥?就是刚才例子中说的threadA线程啊。
    lock.notify_all(thread);
    
    // Ignore pending exception (ThreadDeath), since we are exiting anyway
    thread->clear_pending_exception();
    }
    

    ————————————————
    版权声明:本文为CSDN博主「Mlib」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
    原文链接:https://blog.csdn.net/u010983881/article/details/80257703

    ​ 同理在Thread.join中,也是如此。如果一个线程A执行了thread.join()语句,其含义是:当前线程A等待thread线程终止之后才从thread.join()返回;它的底层实现就是线程A进入到了thread对象的waitset中了,当thread的线程执行完毕后,在线程退出操作中,会自动唤醒阻塞在thread对象上的线程A,这样线程A也就能够继续执行了,表现为线程A等待thread执行完毕后,才接着执行。

    参考链接:Java Thread的join() 原理

    【Java】Thread类中的join()方法原理

    (四)Thread.join的作用和原理

  • 相关阅读:
    unsupported jsonb version number 123
    如何在MPlayer上支持RTSP
    TDengine 时序数据库的 ADO.Net Core 提供程序 Maikebing.EntityFrameworkCore.Taos
    如何使用IoTSharp对接ModBus?
    如何从源码启动和编译IoTSharp
    Asp.Net Core 自动适应Windows服务、Linux服务、手动启动时的内容路径的扩展方法
    MQTTnet 的Asp.Net Core 认证事件的扩展
    Asp.Net Core 中利用QuartzHostedService 实现 Quartz 注入依赖 (DI)
    The remote certificate is invalid according to the validation procedure 远程证书验证无效
    settings插拔式源码
  • 原文地址:https://www.cnblogs.com/cosmos-wong/p/12345299.html
Copyright © 2011-2022 走看看