亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

多線程 yield 方法

1. 前言

本節對 yield 方法進行深入的剖析,主要內容點如下:

  • 首先要了解什么是 CPU 執行權,因為 yield 方法與 CPU 執行權息息相關;
  • 了解 yield 方法的作用,要明確 yield 方法的使用所帶來的運行效果;
  • 了解什么是 native 方法,由于 yield 方法是 native 方法的調用,在學習 yield 方法之前,要了解什么是 native 方法;
  • 掌握 yield 方法如何使用,這是本節知識點的重中之重,一定要著重學習;
  • 了解 yield 方法和 sleep 方法的區別,進行對比記憶,更有助于掌握該方法的獨有特性。

2. 什么是 CPU 執行權

我們知道操作系統是為每個線程分配一個時間片來占有 CPU 的,正常情況下當一個線程把分配給自己的時間片使用完后,線程調度器才會進行下一輪的線程調度,這里所說的 “自己占有的時間片” 即 CPU 分配給線程的執行權。

那進一步進行探究,何為讓出 CPU 執行權呢?
當一個線程通過某種可行的方式向操作系統提出讓出 CPU 執行權時,就是在告訴線程調度器自己占有的時間片中還沒有使用完的部分自己不想使用了,主動放棄剩余的時間片,并在合適的情況下,重新獲取新的執行時間片。

3. yield 方法的作用

方法介紹:Thread 類中有一個靜態的 yield 方法,當一個線程調用 yield 方法時,實際就是在暗示線程調度器當前線程請求讓出自己的 CPU 使用權。

public static native void yield();

Tips:從這個源碼中我們能夠看到如下兩點要點:

  1. yield 方法是一個靜態方法,靜態方法的特點是可以由類直接進行調用,而不需要進行對象 new 的創建,調用方式為 Thread.yield ()。
  2. 該方法除了被 static 修飾,還被 native 修飾,那么進入主題,什么是 native 方法呢?我們繼續來看下文的講解。

抽象地講,一個 Native Method 就是一個 Java 調用的非 Java 代碼的接口。一個 Native Method 是這樣一個 Java 的方法:該方法的實現由非 java 語言實現。

簡單的來說,native 方法就是我們自己電腦的方法接口,比如 Windows 電腦會提供一個 yield 方法,Linux 系統的電腦也同樣會提供一個 yield 方法,本地方法,可以理解為操作調用操作系統的方法接口。

作用:暫停當前正在執行的線程對象(及放棄當前擁有的 cup 資源),并執行其他線程。yield () 做的是讓當前運行線程回到可運行狀態,以允許具有相同優先級的其他線程獲得運行機會。

目的:yield 即 “謙讓”,使用 yield () 的目的是讓具有相同優先級的線程之間能適當的輪轉執行。但是,實際中無法保證 yield () 達到謙讓目的,因為放棄 CPU 執行權的線程還有可能被線程調度程序再次選中。

4. yield 方法如何使用

為了更好的了解 yield 方法的使用,我們首先來設計一個使用的場景。
場景設計

  • 創建一個線程,線程名為 threadOne;
  • 打印一個數,該數的值為從 1 加到 10000000 的和;
  • 不使用 yield 方法正常執行,記錄總的執行時間;
  • 加入 yield 方法,再次執行程序;
  • 再次記錄總執行時間。

期望結果: 未加入 yield 方法之前打印的時間 < 加入 yield 方法之后的打印時間。因為 yield 方法在執行過程中會放棄 CPU 執行權并從新獲取新的 CPU 執行權。

代碼實現 - 正常執行

public class DemoTest extends Thread {
    @Override
    public void run() {
        Long start = System.currentTimeMillis();
        int count = 0;
        for (int i = 1; i <= 10000000; i++) {
             count = count + i;
        }
        Long end = System.currentTimeMillis();
        System.out.println("總執行時間: "+ (end-start) + " 毫秒, 結果 count = " + count);
    }

    public static void main(String[] args) throws InterruptedException {
        DemoTest threadOne = new DemoTest();
        threadOne. start();
    }
}

執行結果驗證

總執行時間: 6 毫秒. 

代碼實現 - yield 執行

public class DemoTest extends Thread {
    @Override
    public void run() {
        Long start = System.currentTimeMillis();
        int count = 0;
        for (int i = 1; i <= 10000000; i++) {
             count = count + i;
             this.yield(); // 加入 yield 方法
        }
        Long end = System.currentTimeMillis();
        System.out.println("總執行時間: "+ (end-start) + " 毫秒. ");
    }

    public static void main(String[] args) throws InterruptedException {
        DemoTest threadOne = new DemoTest();
        threadOne. start();
    }
}

執行結果驗證

總執行時間: 5377 毫秒. 

從執行的結果來看,與我們對 yield 方法的理解和分析完全相符,請同學也進行代碼的編寫和運行,加深學習印象。當加入 yield 方法執行時,線程會放棄 CPU 的執行權,并等待再次獲取新的執行權,所以執行時間上會更加的長。

5. yield 方法和 sleep 方法的區別

  • sleep () 方法給其他線程運行機會時不考慮線程的優先級,因此會給低優先級的線程以運行的機會;
  • yield () 方法只會給相同優先級或更高優先級的線程以運行的機會;
  • 線程執行 sleep () 方法后轉入阻塞 (blocked) 狀態,而執行 yield () 方法后轉入就緒 (ready) 狀態;
  • sleep () 方法聲明會拋出 InterruptedException, 而 yield () 方法沒有聲明任何異常;
  • sleep () 方法比 yield () 方法具有更好的移植性 (跟操作系統 CPU 調度相關)。

6. 小結

在實際的開發場景中,yield 方法的使用場景比較少,但是對于并發原理知識的學習過程,對 yield 方法的了解非常重要,有助于同學了解不同狀態下的線程的不同狀態。
本節要重點掌握 yield 方法的作用以及如何使用 yield 方法。