在Java中,多線程編程是一種強大的并發編程技術,可以讓你同時執行多個任務。Java提供了多種方式來創建和管理線程。以下是Java中給多線程使用的一些主要方法:
-
繼承
Thread
類:- 創建一個新的類繼承自
Thread
類。 - 覆蓋
run()
方法以定義線程的執行行為。 - 創建該類的實例,并調用其
start()
方法來啟動線程。
- 創建一個新的類繼承自
-
實現
Runnable
接口:- 創建一個類實現
Runnable
接口,并實現run()
方法。 - 創建
Thread
類的實例,將實現了Runnable
接口的類的實例作為參數傳給Thread
的構造函數。 - 調用
Thread
實例的start()
方法來啟動線程。
- 創建一個類實現
-
使用
Executor
框架:Executor
框架提供了一種高級的線程管理和任務調度機制。- 可以通過
Executors
類的靜態工廠方法創建不同類型的線程池,如newFixedThreadPool
、newCachedThreadPool
等。 - 使用線程池,可以通過
execute()
方法提交實現了Runnable
接口的任務,或者通過submit()
方法提交實現了Callable
接口的任務。
-
使用
Future
和Callable
:Callable
接口類似于Runnable
,不同之處在于Callable
可以返回結果并能拋出異常。- 提交
Callable
任務給ExecutorService
后,會返回一個Future
對象,通過這個對象可以獲取任務執行結果或取消任務。
-
使用
ForkJoin
框架:ForkJoin
框架專為大規模并行計算設計,它利用工作竊取算法來提高CPU的利用率。- 繼承
RecursiveTask
(有返回值)或RecursiveAction
(無返回值),實現它們的compute
方法來定義分支和合并的邏輯。 - 創建
ForkJoinPool
實例并提交ForkJoinTask
。
以上是Java中實現多線程的一些常用方法及同步機制。根據具體的應用場景和需求,開發者可以選擇適合的方法來實現并發編程。
1.Thread線程使用方法
Thread
是Java中實現多線程程序的基本單位。每個Thread
對象都代表了一個線程的實例。Java中的線程是通過java.lang.Thread
類來實現的。Thread
類本身實現了Runnable
接口,允許你覆蓋其run()
方法,定義線程執行的任務。
Thread
類是實現線程的一種方式。每個Thread
對象代表一個線程的實例。- 通過繼承
Thread
類并重寫其run()
方法來創建一個新的線程。run()
方法定義了線程的執行行為。 - 創建
Thread
類的實例后,可以調用其start()
方法來啟動線程,這會導致run()
方法的執行。
1.1.Thread的核心特性
- 簡單性:直接繼承
Thread
類并覆蓋run()
方法,就可以定義線程的任務。 - 直接控制:由于是直接操作線程對象,可以很容易地管理和控制線程的狀態(如開始、暫停、繼續、中斷)。
- 限制性:由于Java是單繼承的,如果你的類已經繼承了另一個類,就不能通過繼承
Thread
類的方式來創建線程。
1.2.代碼樣例
public class MyThread extends Thread { public MyThread(String threadName){ this.setName(threadName); } public void run() { for (int i = 0; i < 20; i++) { // 打印正在執行的線程名稱 System.out.println(Thread.currentThread().getName() + " Value " + i); } }
}public class ThreadExample { public static void runDemo(){ // 創建線程 MyThread t1 = new MyThread("線程1"); MyThread t2 = new MyThread("線程2"); // 啟動線程 t1.start(); t2.start(); }
}public class DemoMain { public static void main(String[] args) { ThreadExample.runDemo(); }
}
- 輸出結果
線程1 Value 0
線程2 Value 0
線程2 Value 1
線程2 Value 2
線程2 Value 3
線程2 Value 4
線程2 Value 5
線程1 Value 1
線程2 Value 6
線程1 Value 2
線程2 Value 7
線程1 Value 3
線程2 Value 8
線程1 Value 4
線程2 Value 9
線程1 Value 5
線程2 Value 10
線程1 Value 6
線程2 Value 11
線程1 Value 7
線程2 Value 12
線程1 Value 8
線程2 Value 13
線程1 Value 9
線程2 Value 14
線程1 Value 10
線程2 Value 15
線程1 Value 11
線程2 Value 16
線程1 Value 12
線程2 Value 17
線程1 Value 13
線程2 Value 18
線程1 Value 14
線程2 Value 19
線程1 Value 15
線程1 Value 16
線程1 Value 17
線程1 Value 18
線程1 Value 19
1.3.Thread的其他細節操作
1.3.1.等待線程結束(join
)
class ThreadJoinExample extends Thread {public void run() {for (int i = 0; i < 5; i++) {try {Thread.sleep(500); // 模擬執行任務System.out.println(Thread.currentThread().getName() + " index=" + i);} catch (InterruptedException e) {System.out.println("Thread interrupted.");}}}public static void main(String[] args) {ThreadJoinExample t1 = new ThreadJoinExample();ThreadJoinExample t2 = new ThreadJoinExample();t1.start();try {t1.join(); // 等待t1執行完成} catch (InterruptedException e) {e.printStackTrace();}t2.start();}
}
1.3.2.中斷線程(interrupt)
interrupt()
方法用于中斷線程。如果線程因為調用Object.wait()
、Thread.join()
或Thread.sleep()
方法而被阻塞,它將拋出一個InterruptedException
。- 線程可以通過檢查中斷狀態來優雅地終止執行。
class ThreadInterruptExample extends Thread {public void run() {try {for (int i = 0; i < 5; i++) {Thread.sleep(1000); // 模擬執行任務System.out.println(Thread.currentThread().getName() + " index=" + i);}} catch (InterruptedException e) {System.out.println(Thread.currentThread().getName() + " Interrupted");}}public static void main(String[] args) {ThreadInterruptExample t1 = new ThreadInterruptExample();t1.start();try {Thread.sleep(3000); // 讓t1有足夠的時間運行} catch (InterruptedException e) {e.printStackTrace();}t1.interrupt(); // 請求中斷t1線程}
}
1.3.3.設置和檢查線程的優先級
- 線程的優先級通過整數表示,范圍從
Thread.MIN_PRIORITY
(1)到Thread.MAX_PRIORITY
(10)。默認優先級為Thread.NORM_PRIORITY
(5)。 - 可以使用
setPriority(int)
方法來設置線程的優先級,并使用getPriority()
來檢查優先級。
class ThreadPriorityExample extends Thread {public void run() {System.out.println(Thread.currentThread().getName() + " Priority: " + Thread.currentThread().getPriority());}public static void main(String[] args) {ThreadPriorityExample t1 = new ThreadPriorityExample();ThreadPriorityExample t2 = new ThreadPriorityExample();t1.setPriority(Thread.MIN_PRIORITY);t2.setPriority(Thread.MAX_PRIORITY);t1.start();t2.start();}
}
1.3.4.守護線程(Daemon Thread)
- 守護線程是一種在后臺運行的線程,當所有的非守護線程都結束時,守護線程會自動終止。
- 可以通過
setDaemon(true)
方法將線程設置為守護線程,默認情況下,線程是非守護線程。
Thread daemonThread = new Thread(() -> {while (true) {// 執行后臺任務}
});
daemonThread.setDaemon(true); // 將線程設置為守護線程
daemonThread.start();
1.3.5.線程睡眠(Sleep)
Thread.sleep()
方法使當前線程暫停執行一段時間,以毫秒為單位。- 睡眠期間,線程不會釋放鎖,因此其他線程無法訪問被當前線程持有的鎖。
try {Thread.sleep(1000); // 線程睡眠1秒
} catch (InterruptedException e) {e.printStackTrace();
}
1.3.6.線程等待(Wait)與喚醒(Notify)
wait()
方法使線程進入等待狀態,直到其他線程調用相同對象上的notify()
或notifyAll()
方法喚醒它。- 必須在同步塊中使用
wait()
和notify()
方法,否則會拋出IllegalMonitorStateException
異常。
synchronized (object) {object.wait(); // 線程等待
}
synchronized (object) {object.notify(); // 喚醒一個等待線程// 或者使用 object.notifyAll(); 喚醒所有等待線程
}
1.3.7.注意事項
- 避免使用
Thread.stop()
:由于它是不安全的,Java已經棄用了stop()
方法來停止線程。它會導致線程立即終止,而不會進行任何清理操作,可能會產生不一致的狀態。應該使用中斷和檢查機制來優雅地終止線程。 - 優先級只是給線程調度器一個提示:線程的優先級并不能保證執行順序或者執行時間。線程調度器可能會忽略這些優先級,特別是在不同的操作系統上。
- 中斷是一種協作機制:線程需要定期檢查其中斷狀態,并相應地優雅地終止。
2.Runnable線程使用方法
2.1.Runnable的核心特性
Runnable
接口是Java中用于表示可運行任務的標準接口,它是實現多線程的一種方式。其核心特性如下:
-
抽象任務:
Runnable
接口定義了一個抽象方法run()
,用于表示具體的任務邏輯。實現Runnable
接口的類需要實現run()
方法,將具體的任務邏輯放在其中。 -
無返回值:
run()
方法沒有返回值,它是一個void
類型的方法。因此,Runnable
接口表示的是一種無返回值的任務。 -
多線程支持:實現
Runnable
接口的類可以被多個線程共享,多個線程可以同時執行同一個Runnable
對象的run()
方法。 -
線程與任務解耦:
Runnable
接口將任務的定義與線程的實現解耦,使得任務可以在不同的線程中執行,從而提高了代碼的靈活性和可復用性。 -
可與Thread類結合使用:
Runnable
接口通常與Thread
類結合使用,通過創建一個Thread
對象并將Runnable
對象作為參數傳遞給Thread
對象的構造方法,來創建并啟動一個新線程。 -
適用性廣泛:由于其簡單、靈活的特性,
Runnable
接口適用于各種需要并發執行的任務,可以用于各種場景,包括但不限于并行計算、異步處理、任務調度等。
綜上所述,Runnable
接口是實現多線程的一種基本方式,它定義了一個抽象的任務,并提供了一種通用的方式來表示可運行的任務,適用于各種需要并發執行的場景。
2.2. 代碼樣例
public class MyRunnable implements Runnable { public void run() { for (int i = 0; i < 20; i++) { System.out.println(Thread.currentThread().getName() + " Value " + i); } }
}
public class RunnableExample { public static void runDemo(){ // 創建Runnable實例 MyRunnable myRunnable = new MyRunnable(); // 創建Thread對象,并將Runnable實例作為目標對象傳遞給Thread構造器 Thread t1 = new Thread(myRunnable); Thread t2 = new Thread(myRunnable); // 啟動線程 t1.start(); t2.start(); }
}
- 輸出結果
Thread-0 Value 0
Thread-1 Value 0
Thread-0 Value 1
Thread-0 Value 2
Thread-0 Value 3
Thread-1 Value 1
Thread-1 Value 2
Thread-1 Value 3
Thread-1 Value 4
Thread-0 Value 4
Thread-0 Value 5
Thread-0 Value 6
Thread-0 Value 7
Thread-0 Value 8
Thread-0 Value 9
Thread-0 Value 10
Thread-0 Value 11
Thread-0 Value 12
Thread-0 Value 13
Thread-0 Value 14
Thread-0 Value 15
Thread-1 Value 5
Thread-0 Value 16
Thread-1 Value 6
Thread-0 Value 17
Thread-1 Value 7
Thread-0 Value 18
Thread-1 Value 8
Thread-0 Value 19
Thread-1 Value 9
Thread-1 Value 10
Thread-1 Value 11
Thread-1 Value 12
Thread-1 Value 13
Thread-1 Value 14
Thread-1 Value 15
Thread-1 Value 16
Thread-1 Value 17
Thread-1 Value 18
Thread-1 Value 19
3.Thread和Runnable 區別
Thread
和Runnable
是實現多線程程序的兩種基本方式,它們各有特點和適用場景。以下是Thread
與Runnable
之間的主要區別:
3.1. 繼承與實現的區別
- Thread:是一個類,繼承
Thread
類來創建線程意味著你的線程類不能再繼承其他類,因為Java不支持多重繼承。 - Runnable:是一個接口,實現
Runnable
接口如果你的類已經繼承了另一個類,你仍然可以通過實現Runnable
接口來創建線程。
3.2. 設計靈活性
- Thread:直接繼承
Thread
類可能會限制類的擴展性,因為你的類已經繼承了Thread
,它不能再繼承其他類。 - Runnable:實現
Runnable
接口更加靈活,它允許你的類繼承其他類,在多個線程間共享相同的資源。
3.3. 資源共享
- Thread:每個
Thread
類的實例都是一個單獨的對象,它們之間的資源不共享,除非顯式地使用外部資源。 - Runnable:多個線程可以共享同一個
Runnable
實例,因此它們可以訪問相同的資源,這使得在多個線程之間共享數據和行為變得更加容易。
3.4. 適用場景
- Thread:當你的線程不需要訪問共享資源或你只是創建少量線程時,繼承
Thread
類可能是一個簡單的選擇。 - Runnable:當你需要多個線程共享數據或資源,或者你的類已經繼承了另一個類時,實現
Runnable
接口是更好的選擇。
3.5. 推薦做法
雖然兩種方式都可以用來創建線程,但實踐中更推薦實現Runnable
接口,原因包括:
- 實現
Runnable
接口的方式更加靈活,允許繼承其他類并實現多個接口。 - 它支持多個線程共享同一個
Runnable
實例,便于線程間的資源共享。 - 與
Executor
框架結合使用時,通常要求提供Runnable
或Callable
任務,而不是Thread
對象。
總的來說,雖然直接使用Thread
類可以簡化某些編程場景,但實現Runnable
接口提供了更高的靈活性和資源共享的能力,因此在多數情況下更被推薦。
4.Executor線程池的使用方法
Executor
是Java中用于執行任務的框架,它提供了一種將任務提交與任務執行分離的方式,可以更好地管理和控制線程的創建和執行。Executor
框架使得并發編程變得更加簡單和可控,可以有效地利用系統資源,提高程序的性能和可擴展性。
4.1.Executor框架的核心組件
-
Executor接口:
Executor
是一個接口,它定義了單一的方法execute(Runnable command)
,用于執行給定的任務。 -
ExecutorService接口:
ExecutorService
是Executor
的子接口,它擴展了Executor
接口,提供了更豐富的功能。主要包括:- 提交任務并返回
Future
對象,用于追蹤任務的執行狀態和結果。 - 管理線程池的生命周期,包括啟動、關閉、終止等。
- 提交任務并返回
-
ThreadPoolExecutor類:
ThreadPoolExecutor
是ExecutorService
接口的一個具體實現,它是一個靈活的線程池實現,可以根據需要自動調整線程數量。 -
Executors工廠類:
Executors
類是一個工廠類,提供了創建各種類型線程池的靜態方法,如newFixedThreadPool
、newCachedThreadPool
、newSingleThreadExecutor
等。 -
ScheduledExecutorService接口:
ScheduledExecutorService
是ExecutorService
的一個子接口,它提供了可以延遲執行或定期執行任務的功能。
4.2.Executor框架的優點
- 簡化并發編程:
Executor
框架將任務的提交和執行分離開來,簡化了并發編程的復雜性。 - 提高資源利用率:通過使用線程池,可以重復利用已創建的線程,減少了線程的創建和銷毀開銷。
- 提高性能:通過并行執行多個任務,可以加快任務的執行速度,提高程序的性能。
- 提高可管理性:
Executor
框架提供了一系列方法來管理線程池的狀態和行為,如監控線程池的執行狀態、設置線程池的大小等。
4.3.代碼樣例
public class CustomThreadFactory implements ThreadFactory { private String threadNamePrefix; public CustomThreadFactory(String threadNamePrefix) { this.threadNamePrefix = threadNamePrefix; } @Override public Thread newThread(Runnable r) { Thread thread = new Thread(r); // 設置線程名稱 thread.setName(threadNamePrefix + "-" + thread.getId()); return thread; }
}public class ExecutorExample { public static void runDemo() { // 創建自定義的ThreadFactory ThreadFactory threadFactory = new CustomThreadFactory("Xiaocai_CustomThread"); // 創建固定大小的線程池 ExecutorService executor = Executors.newFixedThreadPool(5,threadFactory); // 提交任務 for (Integer i = 0; i < 10; i++) { final Integer threadIndex=i; executor.execute(new MyThread(threadIndex.toString())); } System.out.println("關閉線程池之前"); // 關閉線程池 executor.shutdown(); System.out.println("關閉線程池之后"); try { executor.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("線程池中所有線程執行完畢"); }
}
- 輸出結果
已連接到目標 VM, 地址: ''127.0.0.1:14181',傳輸: '套接字''
Xiaocai_CustomThread-12 Value 0
關閉線程池之前
Xiaocai_CustomThread-14 Value 0
Xiaocai_CustomThread-14 Value 1
Xiaocai_CustomThread-14 Value 2
Xiaocai_CustomThread-14 Value 3
Xiaocai_CustomThread-14 Value 4
Xiaocai_CustomThread-16 Value 0
Xiaocai_CustomThread-16 Value 1
Xiaocai_CustomThread-16 Value 2
Xiaocai_CustomThread-16 Value 3
Xiaocai_CustomThread-12 Value 1
Xiaocai_CustomThread-12 Value 2
Xiaocai_CustomThread-12 Value 3
Xiaocai_CustomThread-12 Value 4
Xiaocai_CustomThread-12 Value 5
Xiaocai_CustomThread-12 Value 6
關閉線程池之后
Xiaocai_CustomThread-16 Value 4
Xiaocai_CustomThread-14 Value 5
Xiaocai_CustomThread-14 Value 6
Xiaocai_CustomThread-16 Value 5
Xiaocai_CustomThread-16 Value 6
Xiaocai_CustomThread-16 Value 7
Xiaocai_CustomThread-16 Value 8
Xiaocai_CustomThread-16 Value 9
Xiaocai_CustomThread-18 Value 0
Xiaocai_CustomThread-20 Value 0
Xiaocai_CustomThread-20 Value 1
Xiaocai_CustomThread-12 Value 7
Xiaocai_CustomThread-20 Value 2
Xiaocai_CustomThread-18 Value 1
Xiaocai_CustomThread-16 Value 10
Xiaocai_CustomThread-16 Value 11
Xiaocai_CustomThread-14 Value 7
Xiaocai_CustomThread-14 Value 8
Xiaocai_CustomThread-14 Value 9
Xiaocai_CustomThread-16 Value 12
Xiaocai_CustomThread-18 Value 2
Xiaocai_CustomThread-18 Value 3
Xiaocai_CustomThread-18 Value 4
Xiaocai_CustomThread-20 Value 3
Xiaocai_CustomThread-12 Value 8
Xiaocai_CustomThread-20 Value 4
Xiaocai_CustomThread-18 Value 5
Xiaocai_CustomThread-16 Value 13
Xiaocai_CustomThread-14 Value 10
Xiaocai_CustomThread-16 Value 14
Xiaocai_CustomThread-18 Value 6
Xiaocai_CustomThread-20 Value 5
Xiaocai_CustomThread-12 Value 9
Xiaocai_CustomThread-20 Value 6
Xiaocai_CustomThread-18 Value 7
Xiaocai_CustomThread-16 Value 15
Xiaocai_CustomThread-14 Value 11
Xiaocai_CustomThread-16 Value 16
Xiaocai_CustomThread-18 Value 8
Xiaocai_CustomThread-20 Value 7
Xiaocai_CustomThread-12 Value 10
Xiaocai_CustomThread-20 Value 8
Xiaocai_CustomThread-18 Value 9
Xiaocai_CustomThread-16 Value 17
Xiaocai_CustomThread-14 Value 12
Xiaocai_CustomThread-16 Value 18
Xiaocai_CustomThread-18 Value 10
Xiaocai_CustomThread-20 Value 9
Xiaocai_CustomThread-12 Value 11
Xiaocai_CustomThread-20 Value 10
Xiaocai_CustomThread-18 Value 11
Xiaocai_CustomThread-16 Value 19
Xiaocai_CustomThread-14 Value 13
Xiaocai_CustomThread-18 Value 12
Xiaocai_CustomThread-20 Value 11
Xiaocai_CustomThread-12 Value 12
Xiaocai_CustomThread-20 Value 12
Xiaocai_CustomThread-18 Value 13
Xiaocai_CustomThread-16 Value 0
Xiaocai_CustomThread-14 Value 14
Xiaocai_CustomThread-16 Value 1
Xiaocai_CustomThread-18 Value 14
Xiaocai_CustomThread-20 Value 13
Xiaocai_CustomThread-12 Value 13
Xiaocai_CustomThread-20 Value 14
Xiaocai_CustomThread-18 Value 15
Xiaocai_CustomThread-16 Value 2
Xiaocai_CustomThread-14 Value 15
Xiaocai_CustomThread-16 Value 3
Xiaocai_CustomThread-18 Value 16
Xiaocai_CustomThread-20 Value 15
Xiaocai_CustomThread-12 Value 14
Xiaocai_CustomThread-20 Value 16
Xiaocai_CustomThread-18 Value 17
Xiaocai_CustomThread-16 Value 4
Xiaocai_CustomThread-14 Value 16
Xiaocai_CustomThread-16 Value 5
Xiaocai_CustomThread-18 Value 18
Xiaocai_CustomThread-20 Value 17
Xiaocai_CustomThread-12 Value 15
Xiaocai_CustomThread-20 Value 18
Xiaocai_CustomThread-18 Value 19
Xiaocai_CustomThread-16 Value 6
Xiaocai_CustomThread-14 Value 17
Xiaocai_CustomThread-16 Value 7
Xiaocai_CustomThread-18 Value 0
Xiaocai_CustomThread-20 Value 19
Xiaocai_CustomThread-12 Value 16
Xiaocai_CustomThread-20 Value 0
Xiaocai_CustomThread-18 Value 1
Xiaocai_CustomThread-16 Value 8
Xiaocai_CustomThread-14 Value 18
Xiaocai_CustomThread-16 Value 9
Xiaocai_CustomThread-18 Value 2
Xiaocai_CustomThread-20 Value 1
Xiaocai_CustomThread-12 Value 17
Xiaocai_CustomThread-20 Value 2
Xiaocai_CustomThread-18 Value 3
Xiaocai_CustomThread-16 Value 10
Xiaocai_CustomThread-14 Value 19
Xiaocai_CustomThread-16 Value 11
Xiaocai_CustomThread-18 Value 4
Xiaocai_CustomThread-20 Value 3
Xiaocai_CustomThread-12 Value 18
Xiaocai_CustomThread-20 Value 4
Xiaocai_CustomThread-18 Value 5
Xiaocai_CustomThread-16 Value 12
Xiaocai_CustomThread-14 Value 0
Xiaocai_CustomThread-16 Value 13
Xiaocai_CustomThread-18 Value 6
Xiaocai_CustomThread-20 Value 5
Xiaocai_CustomThread-12 Value 19
Xiaocai_CustomThread-12 Value 0
Xiaocai_CustomThread-20 Value 6
Xiaocai_CustomThread-18 Value 7
Xiaocai_CustomThread-16 Value 14
Xiaocai_CustomThread-14 Value 1
Xiaocai_CustomThread-16 Value 15
Xiaocai_CustomThread-18 Value 8
Xiaocai_CustomThread-20 Value 7
Xiaocai_CustomThread-12 Value 1
Xiaocai_CustomThread-20 Value 8
Xiaocai_CustomThread-18 Value 9
Xiaocai_CustomThread-16 Value 16
Xiaocai_CustomThread-14 Value 2
Xiaocai_CustomThread-16 Value 17
Xiaocai_CustomThread-18 Value 10
Xiaocai_CustomThread-20 Value 9
Xiaocai_CustomThread-12 Value 2
Xiaocai_CustomThread-20 Value 10
Xiaocai_CustomThread-18 Value 11
Xiaocai_CustomThread-16 Value 18
Xiaocai_CustomThread-14 Value 3
Xiaocai_CustomThread-16 Value 19
Xiaocai_CustomThread-18 Value 12
Xiaocai_CustomThread-20 Value 11
Xiaocai_CustomThread-12 Value 3
Xiaocai_CustomThread-20 Value 12
Xiaocai_CustomThread-18 Value 13
Xiaocai_CustomThread-14 Value 4
Xiaocai_CustomThread-18 Value 14
Xiaocai_CustomThread-20 Value 13
Xiaocai_CustomThread-12 Value 4
Xiaocai_CustomThread-20 Value 14
Xiaocai_CustomThread-18 Value 15
Xiaocai_CustomThread-14 Value 5
Xiaocai_CustomThread-18 Value 16
Xiaocai_CustomThread-20 Value 15
Xiaocai_CustomThread-12 Value 5
Xiaocai_CustomThread-20 Value 16
Xiaocai_CustomThread-18 Value 17
Xiaocai_CustomThread-14 Value 6
Xiaocai_CustomThread-18 Value 18
Xiaocai_CustomThread-20 Value 17
Xiaocai_CustomThread-12 Value 6
Xiaocai_CustomThread-20 Value 18
Xiaocai_CustomThread-18 Value 19
Xiaocai_CustomThread-14 Value 7
Xiaocai_CustomThread-20 Value 19
Xiaocai_CustomThread-12 Value 7
Xiaocai_CustomThread-12 Value 8
Xiaocai_CustomThread-14 Value 8
Xiaocai_CustomThread-12 Value 9
Xiaocai_CustomThread-14 Value 9
Xiaocai_CustomThread-12 Value 10
Xiaocai_CustomThread-14 Value 10
Xiaocai_CustomThread-12 Value 11
Xiaocai_CustomThread-14 Value 11
Xiaocai_CustomThread-12 Value 12
Xiaocai_CustomThread-14 Value 12
Xiaocai_CustomThread-12 Value 13
Xiaocai_CustomThread-14 Value 13
Xiaocai_CustomThread-12 Value 14
Xiaocai_CustomThread-14 Value 14
Xiaocai_CustomThread-12 Value 15
Xiaocai_CustomThread-14 Value 15
Xiaocai_CustomThread-12 Value 16
Xiaocai_CustomThread-14 Value 16
Xiaocai_CustomThread-12 Value 17
Xiaocai_CustomThread-14 Value 17
Xiaocai_CustomThread-12 Value 18
Xiaocai_CustomThread-14 Value 18
Xiaocai_CustomThread-12 Value 19
Xiaocai_CustomThread-14 Value 19
線程池中所有線程執行完畢
與目標 VM 斷開連接, 地址為: ''127.0.0.1:14181',傳輸: '套接字''進程已結束,退出代碼為 0
在上面的示例中,通過Executors.newFixedThreadPool(5)
方法創建了一個固定大小為5的線程池,然后提交了10個任務。線程池會自動管理這些任務的執行,使用合適數量的線程執行任務。最后通過executor.shutdown()
方法關閉線程池。
總之,Executor
框架提供了一種高級的、靈活的方式來執行任務,是Java并發編程中的重要工具之一,值得深入學習和應用。
4.4.細節操作
4.4.1.怎么判斷線程池中所有線程全部運行完畢呢?
System.out.println("關閉線程池之前"); // 關閉線程池 executor.shutdown(); System.out.println("關閉線程池之后"); try { executor.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("線程池中所有線程執行完畢");
4.4.2.調用executor.shutdown()
的目的
調用executor.shutdown()
方法的目的是關閉線程池,即停止接受新的任務,并嘗試將已提交但尚未開始執行的任務從任務隊列中移除,同時等待已經提交的任務執行完畢。主要原因包括:
-
釋放資源:線程池在執行完所有任務后,會保持活動狀態,并且等待新的任務。如果不關閉線程池,線程池中的線程將一直存在,占用系統資源(如內存等),可能導致資源浪費。
-
避免任務丟失:如果不關閉線程池,那么主程序執行完畢后可能會立即結束,導致尚未執行完的任務丟失。
-
優雅關閉:調用
shutdown()
方法后,線程池不會立即關閉,而是等待之前提交的任務執行完成后關閉,保證已提交的任務能夠被執行完畢,避免任務中途被打斷。 -
避免線程泄露:如果不關閉線程池,線程池中的線程可能會一直存在,即使主程序已經執行完畢,導致線程泄露問題。
因此,一般情況下,在不再需要線程池時應該及時調用shutdown()
方法來關閉線程池,以釋放資源并確保已提交的任務能夠正常執行完畢。
5.Future使用方法
Future
是Java中表示異步計算結果的接口,它提供了一種機制,可以在任務執行完成之前先提交任務,并且在需要結果時獲取任務執行的結果。通過Future
,可以在任務執行的過程中進行其他操作,而不必阻塞等待任務執行完成。
5.1.Future的核心特性
-
表示異步計算結果:
Future
接口表示一個異步計算的結果。可以通過get()
方法來獲取計算的結果,如果計算尚未完成,則會阻塞直到計算完成。 -
取消任務:
Future
提供了cancel()
方法用于取消任務的執行。如果任務已經完成,或者由于其他原因無法取消,則cancel()
方法返回false
。 -
判斷任務狀態:
isDone()
方法用于判斷任務是否完成,isCancelled()
方法用于判斷任務是否被取消。
5.2.代碼樣例
public class FutureExample { public static void runDemo() { // 創建線程池 ExecutorService executor = Executors.newFixedThreadPool(1); // 提交任務并獲取Future對象 Future<Integer> future = executor.submit(new Callable<Integer>() { @Override public Integer call() throws Exception { // 模擬一個耗時的任務 Thread.sleep(2000); return 42; } }); // 其他操作... System.out.println("Do something else while waiting for the result..."); try { // 獲取任務結果,如果任務尚未完成,會阻塞直到任務完成 Integer result = future.get(); System.out.println("Result: " + result); } catch (InterruptedException | ExecutionException e) { e.printStackTrace(); } // 關閉線程池 executor.shutdown(); }
}
6.Callable使用方法
Callable
是Java中用于表示可調用任務的接口,與Runnable
接口類似,但它可以返回結果并且能夠拋出異常。通常與ExecutorService
一起使用,通過submit(Callable)
方法提交Callable
任務,并返回一個Future
對象,用于獲取任務的執行結果。
6.1.Callable的核心特性
-
返回結果:
Callable
接口使用泛型來定義返回類型,可以通過call()
方法返回計算的結果。 -
拋出異常:
call()
方法可以拋出異常,允許任務執行過程中發生異常情況。
6.2.使用示例
public class CallableExample { public static void runDemo() { // 創建線程池 ExecutorService executor = Executors.newFixedThreadPool(1); // 提交Callable任務并獲取Future對象 Future<Integer> future = executor.submit(new MyCallable()); // 其他操作... System.out.println("Do something else while waiting for the result..."); try { // 獲取任務結果,如果任務尚未完成,會阻塞直到任務完成 Integer result = future.get(); System.out.println("Result: " + result); } catch (InterruptedException | ExecutionException e) { e.printStackTrace(); } // 關閉線程池 executor.shutdown(); }
}public class MyCallable implements Callable<Integer> { @Override public Integer call() throws Exception { // 模擬一個耗時的任務 Thread.sleep(2000); return 42; }
}
在上面的示例中,首先創建了一個固定大小為1的線程池。然后通過executor.submit(Callable)
方法提交一個Callable
任務,并獲得一個Future
對象。在任務執行的過程中,可以執行其他操作,而不必等待任務完成。當需要任務結果時,調用future.get()
方法,如果任務已經完成,則立即返回結果;如果任務尚未完成,則會阻塞直到任務完成,并返回結果。
總之,Callable
接口允許在任務執行過程中返回結果和拋出異常,提供了一種更加靈活和功能豐富的任務定義方式,適用于需要異步計算結果的場景。
7.ForkJoin使用方法
ForkJoin
框架是Java中用于并行計算的框架,它基于"分而治之"的思想,能夠有效地利用多核處理器的優勢,加速任務的執行速度。ForkJoin
框架提供了ForkJoinPool
、ForkJoinTask
和RecursiveTask
等類來支持并行計算。
ForkJoin框架的核心特性
-
任務分解:
ForkJoin
框架通過遞歸的方式將大任務劃分為小任務,然后并行執行這些小任務,從而實現任務的分解和并行處理。 -
工作竊取:
ForkJoin
框架中的ForkJoinPool
使用工作竊取算法來管理線程池中的工作線程。當一個工作線程沒有任務可執行時,它會從其他工作線程的任務隊列中偷取任務來執行,從而實現負載均衡。 -
遞歸任務:
ForkJoinTask
是ForkJoin
框架中表示任務的抽象類,它有兩個主要子類:RecursiveAction
和RecursiveTask
,分別用于沒有返回值的任務和有返回值的任務。
使用示例
下面是一個使用ForkJoin
框架計算數組元素之和的示例:
public class ForkJoinExample { public static void runDemo() { // 創建ForkJoinPool ForkJoinPool pool = new ForkJoinPool(); // 創建一個大數組 int[] array = new int[1000]; for (int i = 0; i < array.length; i++) { array[i] = i + 1; } // 創建ForkJoin任務 ForkJoinTask<Integer> task = new SumTask(array, 0, array.length); // 提交任務并獲取結果 Integer result = pool.invoke(task); // 輸出結果 System.out.println("Sum of array elements: " + result); // 關閉線程池 pool.shutdown(); }
}class SumTask extends RecursiveTask<Integer> {private int[] array;private int start;private int end;public SumTask(int[] array, int start, int end) {this.array = array;this.start = start;this.end = end;}@Overrideprotected Integer compute() {if (end - start <= 10) {// 如果任務規模小于等于10,直接計算結果int sum = 0;for (int i = start; i < end; i++) {sum += array[i];}return sum;} else {// 否則將任務分解成兩個子任務int mid = (start + end) / 2;SumTask leftTask = new SumTask(array, start, mid);SumTask rightTask = new SumTask(array, mid, end);// 并行執行子任務leftTask.fork();rightTask.fork();// 合并子任務的結果int leftResult = leftTask.join();int rightResult = rightTask.join();// 返回結果return leftResult + rightResult;}}
}
在上面的示例中,首先創建了一個ForkJoinPool
,然后創建了一個包含1000個元素的數組。接著創建了一個ForkJoinTask
(這里使用了RecursiveTask
的子類SumTask
),并將其提交給ForkJoinPool
進行執行。SumTask
任務負責計算數組的元素之和,如果任務規模較小,則直接計算結果;否則將任務分解成兩個子任務,分別計算子數組的元素之和,然后合并結果。
總之,ForkJoin
框架通過任務的分解和并行執行,能夠提高任務的執行效率,特別適用于處理大規模的數據并且任務之間存在遞歸關系的場景。
8.對比一下Thread、Runnable、Executor、Future、Callable、ForkJoin這幾種線程操作的方式,對比他們之間的異同點,以及使用場景
下面是對Thread、Runnable、Executor、Future、Callable和ForkJoin這幾種線程操作方式的異同點以及使用場景的對比:
8.1. Thread
- 方式:直接繼承
Thread
類,覆寫run()
方法。 - 異同點:與其他方式相比,使用起來更為簡單直觀,但線程和任務的耦合性較強,不利于任務的復用和資源的管理。
- 使用場景:適用于簡單的并發任務,不需要共享資源或狀態的場景。
8.2. Runnable
- 方式:實現
Runnable
接口,實現run()
方法。 - 異同點:相比于直接繼承
Thread
類,實現Runnable
接口的方式更為靈活,可以更好地支持任務的復用和資源的管理。 - 使用場景:適用于需要共享資源或狀態的場景,也適用于已經繼承其他類的情況下,無法再繼承
Thread
類。
8.3. Executor
- 方式:通過
Executor
框架來管理和執行任務。 - 異同點:相比于直接創建線程,使用
Executor
框架能夠更好地管理線程資源,提高線程的復用性和可管理性。 - 使用場景:適用于需要管理和調度多個任務的場景,例如批量處理任務、任務隊列等。
8.4. Future
- 方式:通過
Future
接口來獲取異步計算的結果。 - 異同點:
Future
接口提供了異步計算結果的獲取方式,允許任務的提交和獲取結果的操作分離開來,提高了程序的并發性能。 - 使用場景:適用于需要等待異步任務完成并獲取結果的場景,例如并行計算、I/O操作等。
8.5. Callable
- 方式:實現
Callable
接口,實現call()
方法。 - 異同點:與
Runnable
接口類似,但Callable
接口允許任務返回結果和拋出異常,功能更為豐富。 - 使用場景:適用于需要任務返回結果或拋出異常的場景,例如需要等待任務執行完成并獲取結果的場景。
8.6. ForkJoin
- 方式:通過
ForkJoin
框架實現任務的并行計算。 - 異同點:相比于傳統的線程池,
ForkJoin
框架提供了任務分解和工作竊取等機制,能夠更有效地利用多核處理器的優勢。 - 使用場景:適用于需要大規模并行計算的場景,例如遞歸任務的分解、歸并排序等。
綜上所述,不同的線程操作方式有不同的特點和適用場景。直接使用Thread
和Runnable
接口適合簡單的并發任務;Executor
框架適合管理和調度多個任務的場景;Future
和Callable
接口適合需要等待任務完成并獲取結果的場景;ForkJoin
框架適合需要大規模并行計算的場景。在實際應用中,根據具體的需求選擇合適的線程操作方式能夠更好地提高程序的性能和可維護性。