美文网首页java多线程
线程核心方法-Join

线程核心方法-Join

作者: 余生爱静 | 来源:发表于2021-05-06 00:19 被阅读0次

    源码

    /**
         * Waits for this thread to die.
         *
         * <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);
        }
    
     /**
         * Waits at most {@code millis} milliseconds for this thread to
         * die. A timeout of {@code 0} means to wait forever.
         *
         * <p> This implementation uses a loop of {@code this.wait} calls
         * conditioned on {@code this.isAlive}. As a thread terminates the
         * {@code this.notifyAll} method is invoked. It is recommended that
         * applications not use {@code wait}, {@code notify}, or
         * {@code notifyAll} on {@code Thread} instances.
         *
         * @param  millis
         *         the time to wait in milliseconds
         *
         * @throws  IllegalArgumentException
         *          if the value of {@code millis} is negative
         *
         * @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 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()方法的作用,是等待这个线程结束。从源码中不难发现,其实Join()方法最终调用的是wait()方法。

    
    public class ThreadJoin {
        public static void main(String[] args) {
            Thread thread=new Thread(new Runnable() {
                @Override
                public void run() {
                    while (true){
                        System.out.println(""+Thread.currentThread().getName());
                    }
                }
            },"Thread1");
            thread.start();
            try {
                thread.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("任务执行完成");
        }
    }
    
    "main" #1 prio=5 os_prio=0 tid=0x0337dc00 nid=0x2f38 in Object.wait() [0x0162f000]
       java.lang.Thread.State: WAITING (on object monitor)
            at java.lang.Object.wait(Native Method)
            - waiting on <0x0ab717e0> (a java.lang.Thread)
            at java.lang.Thread.join(Thread.java:1252)
            - locked <0x0ab717e0> (a java.lang.Thread)
            at java.lang.Thread.join(Thread.java:1326)
            at com.idea.thread.join.ThreadJoin.main(ThreadJoin.java:17)
    
    
    "Thread1" #9 prio=5 os_prio=0 tid=0x15b5a800 nid=0x1f30 runnable [0x1618f000]
       java.lang.Thread.State: RUNNABLE
            at java.io.FileOutputStream.writeBytes(Native Method)
            at java.io.FileOutputStream.write(FileOutputStream.java:326)
            at java.io.BufferedOutputStream.flushBuffer(BufferedOutputStream.java:82)
            at java.io.BufferedOutputStream.flush(BufferedOutputStream.java:140)
            - locked <0x0ab92a58> (a java.io.BufferedOutputStream)
            at java.io.PrintStream.write(PrintStream.java:482)
            - locked <0x0ab71958> (a java.io.PrintStream)
            at sun.nio.cs.StreamEncoder.writeBytes(StreamEncoder.java:221)
            at sun.nio.cs.StreamEncoder.implFlushBuffer(StreamEncoder.java:291)
            at sun.nio.cs.StreamEncoder.flushBuffer(StreamEncoder.java:104)
            - locked <0x0ab92a98> (a java.io.OutputStreamWriter)
            at java.io.OutputStreamWriter.flushBuffer(OutputStreamWriter.java:185)
            at java.io.PrintStream.newLine(PrintStream.java:546)
            - locked <0x0ab71958> (a java.io.PrintStream)
            at java.io.PrintStream.println(PrintStream.java:807)
            - locked <0x0ab71958> (a java.io.PrintStream)
            at com.idea.thread.join.ThreadJoin$1.run(ThreadJoin.java:11)
            at java.lang.Thread.run(Thread.java:748)
    
    

    有很多人不理解join为什么阻塞的是主线程呢? 不理解的原因是阻塞主线程的方法是放在thread这个实例作用,让大家误以为应该阻塞thread线程。实际上主线程会持有thread这个对象的锁,然后调用wait方法去阻塞,而这个方法的调用者是在主线程中的,所以造成主线程阻塞。

    public class ThreadJoin {
        public static void main(String[] args) {
            Thread current = Thread.currentThread();
            Thread thread = new Thread(new Runnable() {
                @Override
                public void run() {
                    while (true) {
                        System.out.println("" + Thread.currentThread().getName());
                    }
                }
            }, "Thread1");
            thread.start();
            synchronized (current) {
                try {
                    current.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("任务执行完成");
        }
    }
    

    Join方法实质就是调用当前线程的wait()方法

    有了wait(),必然有notify(),什么时候才会notify呢?在jvm源码里:

    // 位于/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();
    }
    

    当子线程thread1执行完毕的时候,jvm会自动唤醒阻塞在thread1对象上的线程,在我们的例子中也就是主线程。至此,thread1线程对象被notifyall了,那么主线程也就能继续跑下去了。

    总结

    join() 是一个synchronized方法, 里面调用了wait(),这个过程的目的是让持有这个同步锁的线程进入等待,那么谁持有了这个同步锁呢?答案是主线程,因为主线程调用了thread1.join()方法,相当于在thread1.join()代码这块写了一个同步代码块,谁去执行了这段代码呢,是主线程,所以主线程被wait()了。然后在子线程thread1执行完毕之后,JVM会调用lock.notify_all(thread);唤醒持有thread1这个对象锁的线程,也就是主线程,会继续执行。

    相关文章

      网友评论

        本文标题:线程核心方法-Join

        本文链接:https://www.haomeiwen.com/subject/ltisrltx.html