1. 程式人生 > >執行緒方法join

執行緒方法join

xl_echo編輯整理,歡迎轉載,轉載請宣告文章來源。更多IT、程式設計案例、資料請聯絡QQ:1280023003,加群298140694。百戰不敗,依不自稱常勝,百敗不頹,依能奮力前行。——這才是真正的堪稱強大!!!


執行緒的方法join,其實就是一個多執行緒相互制約的的行為。如:當執行緒A使用join,同事執行的執行緒B就會等待,知道A執行緒的生命週期結束。但是這個例子有個前提,需要至少兩條以上的執行執行緒,並且這兩條執行緒要有執行緒呼叫。

在Thread的原始碼中我們可以看到join的實現。

/**
     * 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; } } } /** * Waits at most {@code millis} milliseconds plus * {@code nanos} nanoseconds for this thread to die. * * <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 * * @param nanos * {@code 0-999999} additional nanoseconds to wait * * @throws IllegalArgumentException * if the value of {@code millis} is negative, or the value * of {@code nanos} is not in the range {@code 0-999999} * * @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, int nanos) throws InterruptedException { if (millis < 0) { throw new IllegalArgumentException("timeout value is negative"); } if (nanos < 0 || nanos > 999999) { throw new IllegalArgumentException( "nanosecond timeout value out of range"); } if (nanos >= 500000 || (nanos != 0 && millis == 0)) { millis++; } join(millis); } /** * 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); }

可以看到的是該方法被三次過載。作用如下:

  • 第一個被使用會在該執行緒會給該執行緒執行完成的優先權。
  • 第二個被使用會讓該執行緒在多少毫秒之內有優先權。
  • 第三個被使用會讓該執行緒在多少毫秒和多少納秒之內擁有優先權。比第二個更精確。

仔細閱讀我們不難發現join底層其實是呼叫了wait()方法,實現的時候就是讓呼叫程進入了A物件的等待池,等到A物件執行完成之後,呼叫的執行緒才能出來去掉用B執行緒。

我們可以通過一個例項來看看

package com.echo.es.demoes.JavaApi;

import org.elasticsearch.client.transport.TransportClient;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.transport.InetSocketTransportAddress;
import org.elasticsearch.transport.client.PreBuiltTransportClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.net.InetAddress;
import java.net.UnknownHostException;

/**
 * @Author xl_echo
 * @Date 2018/8/15 下午8:30
 **/
@Configuration
public class TestClient {

  public static void main(String[] args) throws InterruptedException {

    Thread t1 = new Thread(() -> {
      while (true) {
        try {
          Thread.sleep(1000L);
        } catch (InterruptedException e) {
          e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + "執行緒執行中");
      }
    });

    Thread t2 = new Thread(() -> {
      while (true) {
        try {
          Thread.sleep(1000L);
        } catch (InterruptedException e) {
          e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + "執行緒執行中");
      }
    });

    t1.start();
//    t1.join();
    t2.start();
//    t2.join();
    System.out.println("main執行緒結束");
  }

}

當t1.join()和t2.join()全部註釋掉的時候我們可以看到控制檯輸出效果如下:main執行緒並沒有因為死迴圈而不繼續往下執行,而是在呼叫之後結束,但是t1和t2在不間斷執行。
這裡寫圖片描述

當我們開啟t1.join()的時候我們可以看到main執行緒一直沒有被結束,控制檯資料的結果是顯示t1一直在執行。其實是main執行緒一直等待t1執行完成。
這裡寫圖片描述

但是當我們註釋掉t1.join(),開啟t2.join()的註釋時,我們可以我們可以看到t1一直在執行,但是t2也一直在執行。輸出結果如下:
這裡寫圖片描述

很多人在這裡有誤解,以為開啟t2.join()的時候,t1不會執行,main也會一直等待,其實這是執行緒的主從關係沒有理解清楚。當我們開啟t2.join()的註釋時,main呼叫玩t1,會繼續呼叫t2,但是t2使用了join方法,所以這個時候main執行緒會進入t2的等待池,等待t2結束,main才會結束。但是t1呢?他是一個已經被啟動的執行緒,如果沒有結束,不管t2的狀態如何,他都會繼續執行。

總結:

所以到這裡我們不難看出,執行緒的join方法,其實就是讓呼叫執行緒等待。