java多线程

线程核心方法-Join

2021-05-06  本文已影响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这个对象锁的线程,也就是主线程,会继续执行。

上一篇 下一篇

猜你喜欢

热点阅读