你好!今天我們繼續講多線程。讓我們檢查 Thread 類及其一些方法的作用。以前我們研究類方法的時候,一般都是這麼寫的:<方法名> -> <方法的作用>。
這不適用於
讓我們回憶一下上節課的例子:
該
該

Thread
的方法 :) 它們具有更複雜的邏輯,如果沒有幾個示例,您將無法理解。
Thread.start() 方法
讓我們從重複自己開始。Thread
您可能還記得,您可以通過讓您的類繼承類並覆蓋方法來創建線程run()
。但它當然不會自行啟動。為此,我們調用對象的start()
方法。 
public class MyFirstThread extends Thread {
@Override
public void run() {
System.out.println("Thread executed: " + getName());
}
}
public class Main {
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
MyFirstThread thread = new MyFirstThread();
thread.start();
}
}
}
注意:要啟動一個線程,必須調用特殊的start()
方法而不是run()
方法!這是一個很容易犯的錯誤,尤其是當您剛開始學習多線程時。在我們的示例中,如果您調用該run()
方法 10 次而不是start()
,您將得到:
public class Main {
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
MyFirstThread thread = new MyFirstThread();
thread.run();
}
}
}
看我們程序的執行結果: 執行的線程:Thread-0 執行的線程:Thread-1 執行的線程:Thread-2 執行的線程:Thread-3 執行的線程:Thread-4 執行的線程:Thread-5 執行的線程:Thread-6執行的線程:Thread-7 執行的線程:Thread-8 執行的線程:Thread-9 查看輸出的順序:一切都以完美的順序發生。很奇怪吧?我們對此並不習慣,因為我們已經知道線程啟動和執行的順序是由操作系統內部的高級智能決定的:線程調度程序。也許我們只是運氣好?當然,這與運氣無關。您可以通過多次運行該程序來驗證這一點。問題是調用run()
方法直接與多線程無關。在這種情況下,程序將在主線程上執行,即執行該main()
方法的線程。它只是在控制台上連續打印 10 行,僅此而已。10 個線程尚未啟動。所以,以後要記住這一點,不斷地檢查自己。如果要run()
調用該方法,請調用start()
. 讓我們更進一步。
Thread.sleep() 方法
要暫時暫停當前線程的執行,我們使用 方法sleep()
。 
sleep()
方法以毫秒數作為參數,它表示讓線程休眠的時間量。
public class Main {
public static void main(String[] args) throws InterruptedException {
long start = System.currentTimeMillis();
Thread.sleep(3000);
System.out.println(" - How long did I sleep? \n - " + ((System.currentTimeMillis()-start)) / 1000 + " seconds");
}
}
控制台輸出: - 我睡了多長時間?- 3 秒 注意:該sleep()
方法是靜態的:它使當前線程休眠。也就是當前正在執行的那個。這是另一個重點:休眠線程可以被中斷。在這種情況下,程序會拋出一個InterruptedException
. 我們將考慮下面的示例。順便問一下,線程喚醒後會發生什麼?它會從中斷的地方繼續執行嗎?No. 線程喚醒後,即作為參數傳遞的時間已經過去Thread.sleep()
,它會轉換為可運行狀態。但是,這並不意味著線程調度程序會運行它。它很可能會優先選擇其他一些非睡眠線程,並允許我們剛剛喚醒的線程稍後繼續工作。一定要記住這一點:起床不代表馬上繼續工作!
Thread.join() 方法

join()
方法暫停當前線程的執行,直到另一個線程完成。如果我們有 2 個線程,t1
並且t2
,我們寫
t1.join()
然後直到完成它的工作t2
才會開始。t1
該join()
方法可用於保證線程的執行順序。讓我們join()
在以下示例中考慮該方法的工作原理:
public class ThreadExample extends Thread {
@Override
public void run() {
System.out.println("Thread started: " + getName());
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Thread " + getName() + " is finished.");
}
}
public class Main {
public static void main(String[] args) throws InterruptedException {
ThreadExample t1 = new ThreadExample();
ThreadExample t2 = new ThreadExample();
t1.start();
/* The second thread (t2) will start running only after the first thread (t1)
is finished (or an exception is thrown) */
try {
t1.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
t2.start();
// The main thread will continue running only after t1 and t2 have finished
try {
t1.join();
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("All threads have finished. The program is finished.");
}
}
我們創建了一個簡單的ThreadExample
類。它的任務是顯示線程已經啟動的消息,然後休眠 5 秒,最後報告工作完成。小菜一碟。主要邏輯在Main
類中。看評論:我們用join()
方法成功管理了線程的執行順序。如果您還記得我們是如何開始這個主題的,執行順序是由線程調度程序處理的。它自行決定運行線程:每次都以不同的方式運行。這裡我們使用的方法是保證t1
線程先啟動先執行,然後t2
線程,只有在那之後程序的主線程才會繼續。繼續。在實際程序中,您經常會發現需要中斷線程執行的情況。例如,我們的線程正在運行,但它正在等待某個事件或條件。如果它發生,則線程停止。如果有某種stop()
方法,這可能是有意義的。但這不是那麼簡單。曾幾何時,Java 確實有一個Thread.stop()
方法,允許一個線程被中斷。但後來從 Java 庫中刪除了它。你可以在 Oracle 文檔中找到它並看到它被標記為已棄用. 為什麼?因為它只是停止了線程而沒有做任何其他事情。例如,線程可能正在處理數據並更改某些內容。然後在它工作的過程中,它突然被這個stop()
方法毫不客氣地切斷了。沒有適當的關閉,也沒有資源的釋放,甚至沒有錯誤處理——這些都沒有。稍微誇張一點,這個stop()
方法簡直就是摧毀了所有擋路的東西。這就像從插座上拔下電源線來關閉計算機一樣。是的,你可以得到想要的結果。但是每個人都知道,幾週後計算機不會感謝您以這種方式對待它。這就是為什麼中斷線程的邏輯在 Java 中發生了變化,現在使用了一種特殊的interrupt()
方法。
Thread.interrupt() 方法
interrupt()
如果在線程上調用該方法會發生什麼?有兩種可能性:
- 如果對象處於等待狀態,例如,由於
join
或sleep
方法,則等待將被中斷,程序將拋出一個InterruptedException
. - 如果線程處於運行狀態,則將在
interrupted
對像上設置布爾標誌。
Thread
類具有boolean isInterrupted()
方法的原因。讓我們回到基礎課程中的時鐘示例。為了方便起見,我們稍微簡化了它:
public class Clock extends Thread {
public static void main(String[] args) throws InterruptedException {
Clock clock = new Clock();
clock.start();
Thread.sleep(10000);
clock.interrupt();
}
public void run() {
Thread current = Thread.currentThread();
while (!current.isInterrupted())
{
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
System.out.println("The thread was interrupted");
break;
}
System.out.println("Tick");
}
}
}
在這種情況下,時鍾啟動並開始每秒滴答作響。在第 10 秒,我們中斷時鐘的線程。 如您所知,如果我們嘗試中斷的線程處於其中一種等待狀態,則結果為InterruptedException
. 這是一個已檢查的異常,所以我們可以很容易地捕獲它並執行我們的邏輯來完成程序。而這正是我們所做的。這是我們的結果: Tick Tick Tick Tcik Tick Tick Tick Tick Tick 線程被中斷了 我們對Thread
類最重要方法的介紹到此結束。祝你好運!
GO TO FULL VERSION