Нижняя строка join() Java Thread

Java задняя часть исходный код

0.Join()

Смысл слияния потоковОбъединить потоки нескольких параллельных потоков в однопоточное выполнение, сценарий примененияКогда один поток должен дождаться завершения выполнения другого потока, прежде чем он сможет выполнить, класс Thread предоставляет метод соединения для выполнения этой функции, обратите внимание, что онне статический метод.

join имеет 3 перегруженных метода:

void join():当前线程等该加入该线程后面,等待该线程终止。
void join(long millis):当前线程等待该线程终止的时间最长为 millis 毫秒。 如果在millis时间内,该线程没有执行完,那么当前线程进入就绪状态,重新等待cpu调度。
void join(long millis,int nanos):等待该线程终止的时间最长为 millis 毫秒 + nanos纳秒。如果在millis时间内,该线程没有执行完,那么当前线程进入就绪状态,重新等待cpu调度。

Ссылаться на:Ape Code Road: грызть и параллелизм (2)

1. Как использовать

Создайте новый класс Thread и переопределите метод run():

public class MyThread extends Thread {

    @Override
    public void run() {
        System.out.println("子线程执行完毕");
    }
}

Создайте новый тестовый класс и протестируйте метод Join():

public class TestThread {

    public static void main(String[] args) {
        //循环五次
        for (int i = 0; i < 5; i++) {

            MyThread thread = new MyThread();
            //启动线程
            thread.start();
            try {
                //调用join()方法
                thread.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("主线程执行完毕");
            System.out.println("~~~~~~~~~~~~~~~");

        }
    }
}

Результат выглядит следующим образом:

子线程执行完毕
主线程执行完毕
~~~~~~~~~~~~~~~
子线程执行完毕
主线程执行完毕
~~~~~~~~~~~~~~~
子线程执行完毕
主线程执行完毕
~~~~~~~~~~~~~~~
子线程执行完毕
主线程执行完毕
~~~~~~~~~~~~~~~
子线程执行完毕
主线程执行完毕
~~~~~~~~~~~~~~~

Анализ результатов:Дочерний поток каждый раз выполняется перед основным потоком, то есть дочерний поток будет выполняться перед основным потоком.

代码中循环5次是为了排除线程执行的随机性,若不能说明问题,可以调大循环次数进行测试。

2. Принцип анализ

Просмотр класса потока Источник:

public final void join() throws InterruptedException {
    //当调用join()时,实际是调用join(long)方法
        join(0);
    }

Просмотрите исходный код метода Join(long):

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) {  //由于上一步传入参数为0,因此调用当前判断
            while (isAlive()) { //判断子线程是否存活
                wait(0); //调用wait(0)方法
            }
        } else {
            while (isAlive()) {
                long delay = millis - now;
                if (delay <= 0) {
                    break;
                }
                wait(delay);
                now = System.currentTimeMillis() - base;
            }
        }
    }

Просмотрите исходный код метода isAlive():

/**
     * Tests if this thread is alive. A thread is alive if it has
     * been started and has not yet died.
     * 测试线程是否还活着。如果线程存活的话它就是已经开始,还没有死亡的状态。
     * @return  <code>true</code> if this thread is alive;
     *          <code>false</code> otherwise.
     */****
    public final native boolean isAlive();

инструкция:Этот метод является локальным методом для определения того, является ли объект потока живым или нет.Если объект потока вызывает метод start(), суждение истинно, если объект потока не умирает. В приведенном выше примере, поскольку вызывается метод start() дочернего потока, а операция не завершена, суждение верно.

Просмотрите исходный код метода wait():

public final native void wait(long timeout) throws InterruptedException;

инструкция:Этот метод является собственным методом. Текущий поток, вызывающий этот метод, должен снять блокировку и дождаться пробуждения. В приведенном выше примере основной поток вызывает метод join() объекта дочернего потока, поэтому основной поток должен снять блокировку в этой позиции и ждать.

wait()与wait(0)的区别:
查看wait()方法源码,wait()方法只调用了wait(0),如下:

public final void wait() throws InterruptedException {
        wait(0);
}

因此,wait()与wait(0)相同。

Давайте взглянем на вышеуказанные шаги:Основной поток ждет(), дочерний поток вызывает run() для выполнения и печатает "дочерний поток завершил выполнение". На данный момент основной поток не пробужден и следующие операции не выполнены. Итак, возникает вопрос,ВОЗ? во сколько? Разбудить основной поток?

Убедитесь, что метод exit() существует в классе Thread.Исходный код выглядит следующим образом:

 /**
     * This method is called by the system to give a Thread
     * a chance to clean up before it actually exits.
     * 这个方法由系统调用,当该线程完全退出前给它一个机会去释放空间。
     */
private void exit() {
        if (group != null) {                //线程组在Thread初始化时创建,存有创建的子线程
            group.threadTerminated(this);   //调用threadTerminated()方法
            group = null;
        }
        /* Aggressively null out all reference fields: see bug 4006245 */
        target = null;
        /* Speed the release of some of these resources */
        threadLocals = null;
        inheritableThreadLocals = null;
        inheritedAccessControlContext = null;
        blocker = null;
        uncaughtExceptionHandler = null;
    }

При отладке exit() будет вызываться после того, как поток выполнит метод run().В это время в группе потоков есть текущий дочерний поток, поэтому будет вызван метод threadTerminated() группы потоков. Просмотрите исходный код метода ThreadGroup.threadTerminated():

/** Notifies the group that the thread {@code t} has terminated.
  * 通知线程组,t线程已经终止。
  *
void threadTerminated(Thread t) {
        synchronized (this) {
            remove(t);                          //从线程组中删除此线程

            if (nthreads == 0) {                //当线程组中线程数为0时
                notifyAll();                    //唤醒所有待定中的线程
            }
            if (daemon && (nthreads == 0) &&
                (nUnstartedThreads == 0) && (ngroups == 0))
            {
                destroy();
            }
        }
    }

С помощью этого метода дочерний поток удаляется из группы потоков, иразбудить другие ожидающие потоки. В приведенном выше примере дочерний поток в это время уничтожается, занятые ресурсы освобождаются, а ожидающий поток пробуждается. Основной поток, ожидающий в методе join(), просыпается, получает блокировку и печатает «выполнение основного потока завершено».

3. Резюме

Метод Join() заставляет поток, вызывающий этот метод, ждать() (в данном примере основной поток) до тех пор, пока поток (в данном примере дочерний поток), где находится объект потока (в данном примере объект MyThread), который вызывает этот метод, выполняется. Просыпайтесь, когда закончите.

由于线程的启动与销毁其实是由操作系统进行操作,所以在描述的时候刻意略去,如果有疑惑的地方,可以查看C++编写的本地方法。