標籤:
基本概念
線程與任務的概念不一樣。
任務:通常是一些抽象的且離散的工作單元,比如在Web請求中,針對使用者的請求需要返回相應的頁面是一個任務,在Java中實現Runnable介面的類也是一個任務.
線程:執行任務的實體,可以在單個線程中串列地執行各項任務,例如單線程串列執行Web請求,也可以在為每個請求建立一個線程執行。
任務是一組邏輯工作單元,而線程則是使任務非同步執行的機制。
一個簡單的例子:當我們定義了一個Runnable對象時,即任務,使用new Thread(Runnable).start()即將任務放到實際的線程中執行。
Executor線程池
Web應用中有兩種執行任務的策略:
所以在Java中引入了Executor(線程池)架構來簡化線程的管理工作,Executor使用Runnable作為其基本的任務表示形式,在Java類庫中,任務執行的主要抽象不是Thread,而是Executor。
Executor基於生產者-消費者模式,提交任務的操作相當於生產者(產生待完成的工作單元),執行任務的線程則相當於消費者(執行完這些工作單元),這樣就將請求處理任務的提交與任務的實際執行解耦開來。在下面例子中建立一個固定長度的線程池,可以容納100個線程。
class TaskExecutionWebServer { private static final int NTHREADS = 100; // 調用Executors中的靜態Factory 方法可以來建立一個線程池 private static final Executor exec = Executors.newFixedThreadPool(NTHREADS); public static void main(String[] args) throws IOException { ServerSocket socket = new ServerSocket(80); while (true) { final Socket connection = socket.accept(); Runnable task = new Runnable() { public void run() { handleRequest(connection); } }; // 通過execute將任務提交到工作隊列中,背景工作執行緒反覆擷取執行 exec.execute(task); } }}
執行策略
線程池是指管理一組同構背景工作執行緒的資源集區。通過重用現有的線程而不是建立新線程,可以在處理多個請求時,分攤線上程建立和銷毀過程中產生的巨大開銷。另一個額外的好處是,當請求到達時,背景工作執行緒已經存在,因為提高了響應性。
- 工作隊列(Work Queue):線程池是與工作隊列密切相關的,其中在工作隊列中儲存了所有等待執行的任務。
- 工作者線程(Worker Thread):從工作隊列中擷取一個任務,執行任務,然後返回線程池並等待下一個任務。
在執行策略中定義了任務執行的“What、Where、When、How”等方面,包括:
- 該任務在哪個線程中執行
- 任務可以按照什麼樣的順序來執行(如優先順序、FIFO、LIFO)
- 有多少個任務可以並發執行
- ......
生命週期
線程池生命週期(ExecutorService)
我們已經知道了如何建立一個Executor,但並沒有討論如何關閉它。為瞭解決執行服務的生命週期問題,Executor擴充了ExecutorService介面,添加了一些用於生命週期管理方法。
public interface ExecutorService extends Executor { void shutdown(); List<Runnable> shutdownNow(); boolean isShutdown(); //...}
ExecutorService的生命週期有3種狀態:運行、關閉和已終止。shutdown()方法將執行平緩的關閉過程:不再接受新的任務,同時等待已提交任務執行完成(包括未開始執行的任務)。shutdownNow()將嘗試取消所有啟動並執行任務。關閉後再提交,由“拒絕執行處理器”處理,或者拋出RejectedExecutionException異常。
任務生命週期(Future)
由於Runnable不能返回一個值或者拋出一個受檢查的異常,存在著很大的局限性。所以引入了Callable,它認為主進入點(即call)將返回一個值,並可能拋出一個異常。
又Runnable和Callable描述都是抽象的計算任務,通常經過建立、提交、開始和完成四個階段,就如ExecutorService為Executor解決生命週期問題,Future也為一個任務解決了生命週期的問題,並提供了相應地方法來判斷是否已經完成或取消,以及擷取任務的結果和取消任務。
public interface Callable<V> { V call() throws Exception;}public interface Future<V> { boolean cancel(boolean mayInterruptIfRunning); // 取消任務 boolean isCancelled(); boolean isDone(); V get() throws InterruptedException, ExecutionExeception, CancellationExeception; V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionExeception CancellationExeception, TimeoutException;
通過調用ExecutorService中得submit方法將返回一個Future代表該任務的生命週期,可用來獲得任務的執行結果或者取消任務。也可以顯示地為某個指定的Runnable或Callable執行個體化一個FutureTask。
在將Runnable或Callable提交到Executor的過程中,包含了一個安全發布的過程,即將Runnable或Callable從提交線程發布到最終執行任務的線程。類似地,在擷取Future結果的過程中也包含了一個安全發布,即將這個結果從計算它的線程發布到任何通過get獲得它的線程。
參考: 書本Java Concurrency in Practice
Java多線程基本概念