前言。彼佳叔叔
假設我們想裝一瓶水。提供一瓶和Uncle Petya 的水。今天佩蒂亞叔叔安裝了一個新水龍頭,他一直稱讚它很漂亮。在此之前,他只使用一個舊的、堵塞的水龍頭,因此裝瓶處的生產線非常長。摸索了一下,從溢出的方向傳來了灌水的聲音,兩分鐘後,瓶子還處於灌水階段,身後已經聚集了平常的隊列,腦海中浮現出大叔多麼貼心的畫面。 Petya 只選擇最好的H2O分子放入我們的瓶子中。受過生活訓練的佩蒂亞叔叔會安撫那些特別好鬥的人,並承諾盡快完成任務。喝完一瓶後,他又拿起下一瓶,並打開通常的壓力,這並沒有顯示出新水龍頭的所有功能。人家不高興了...
理論
多執行緒是平台在單一進程中創建多個執行緒的能力。創建和執行線程比創建進程簡單得多,因此如果需要在一個程式中實現多個並行操作,則需要使用額外的執行緒。在JVM中,任何程式都在主執行緒中運行,其餘程式都從主執行緒啟動。在同一進程內,執行緒之間可以交換資料。當啟動一個新線程時,可以使用以下方法將其聲明為用戶線程
setDaemon(true);
如果沒有其他正在運行的線程,則此類線程將自動終止。執行緒具有工作優先權(優先權的選擇並不能保證最高優先權執行緒比較低優先權執行緒更快完成)。
- MIN_PRIORITY
- NORM_PRIORITY(預設)
- 最大優先權
使用串流時的基本方法:
run()
– 執行緒
start()
– 啟動一個執行緒
getName()
– 傳回線程名稱
setName()
– 指定流的名稱
wait()
notify()
– 繼承的方法,執行緒等待從另一個執行緒呼叫該方法
notify()
– 繼承的方法,恢復先前停止的執行緒
notifyAll()
– 繼承的方法,恢復先前停止的執行緒
sleep()
– 將串流暫停指定時間
join()
– 等待執行緒完成
interrupt()
– 中斷執行緒執行
更多方法可以在這裡找到
。 如果你的程式有以下情況,那麼是時候考慮新主題了:
執行緒類
Java 中的線程表示為一個類別
Thread
及其後代。下面的範例是流類別的簡單實作。
import static java.lang.System.out;
public class ExampleThread extends Thread{
public static void main(String[] args) {
out.println("Основной поток");
new ExampleThread().start();
}
@Override
public void run() {
out.println("Новый поток");
}
}
結果我們得到
Основной поток
Новый поток
在這裡,我們創建了我們的類別並使其成為該類別的後代
Thread
,然後編寫 main() 方法來啟動主線程並重寫
run()
類別方法
Thread
。現在,在創建了類別的實例並執行了其繼承的方法後,
start()
我們將啟動一個新線程,在該線程中將執行方法體中描述的所有內容
run()
。聽起來很複雜,但是看看範例程式碼一切都應該很清楚。
可運行介面
Oracle還建議實作該介面來啟動新線程
Runnable
,這比上一個範例中唯一可用的繼承為我們提供了更多的設計彈性(如果您查看該類別的源代碼,您可以看到
Thread
它也實現了該接口
Runnable
)。讓我們使用建議的方法來建立新線程。
import static java.lang.System.out;
public class ExampleRunnable implements Runnable {
public static void main(String[] args) {
out.println("Основной поток");
new Thread(new ExampleRunnable()).start();
}
@Override
public void run() {
out.println("Новый поток");
}
}
結果我們得到
Основной поток
Новый поток
這些例子非常相似,因為 在編寫程式碼時,我們必須實作
run()
介面中描述的抽象方法
Runnable
。啟動新線程有點不同。
Thread
我們透過將介面實作實例的參考作為參數傳遞來建立該類別的實例
Runnable
。正是這種方法允許您創建新線程而無需直接繼承該類別
Thread
。
長時間操作
下面的例子將清楚地展示使用多執行緒的好處。假設我們有一個簡單的任務,需要多次冗長的計算,在本文之前,我們會用一種方法來解決它,也許將其分解為單獨的方法以便於感知,甚至可能是類,但本質是
main()
相同的。所有操作都將依序依序執行。讓我們模擬重量級計算並測量它們的執行時間。
public class ComputeClass {
public static void main(String[] args) {
long startTime = System.currentTimeMillis();
for(double i = 0; i < 999999999; i++){
}
System.out.println("complete 1");
for(double i = 0; i < 999999999; i++){
}
System.out.println("complete 2");
for(double i = 0; i < 999999999; i++){
}
System.out.println("complete 3");
long timeSpent = System.currentTimeMillis() - startTime;
System.out.println("программа выполнялась " + timeSpent + " миллисекунд");
}
}
結果我們得到
complete 1
complete 2
complete 3
программа выполнялась 9885 миллисекунд
執行時間還有很多不足之處,一直以來我們都在看著一個空白的輸出屏幕,情況與有關 Petya 叔叔的故事非常相似,只是現在在他的角色中,我們開發人員還沒有利用現代設備的所有功能。我們會改進。
public class ComputeClass {
public static void main(String[] args) {
long startTime = System.currentTimeMillis();
new MyThread(1).start();
new MyThread(2).start();
for(double i = 0; i < 999999999; i++){
}
System.out.println("complete 3");
long timeSpent = System.currentTimeMillis() - startTime;
System.out.println("программа выполнялась " + timeSpent + " миллисекунд");
}
}
class MyThread extends Thread{
int n;
MyThread(int n){
this.n = n;
}
@Override
public void run() {
for(double i = 0; i < 999999999; i++){
}
System.out.println("complete " + n);
}
}
結果我們得到
complete 1
complete 2
complete 3
программа выполнялась 3466 миллисекунд
運行時間已顯著減少(在不支援多執行緒的處理器上可能無法實現此效果,甚至可能增加執行時間)。值得注意的是,線程可能會無序結束,如果開發人員需要操作的可預測性,他必須針對特定情況獨立實現。
執行緒組
Java 中的執行緒可以組合成群組;該類別就是用於此目的的
ThreadGroup
。群組可以包括單一線程和整個群組。如果您需要中斷關聯的流(例如,在連接丟失時與網路相關的流),這會很方便。
您可以在此處閱讀有關群組的更多信息, 我希望您現在已經更清楚該主題,並且您的用戶會感到高興。
GO TO FULL VERSION