Effective Java 第三版——80. EXECUTORS, TASKS, STREAMS 優於線程

Tips
書中的源代碼地址:https://github.com/jbloch/effective-java-3e-source-code
注意,書中的有些代碼裏方法是基於Java 9 API中的,因此JDK 最好下載 JDK 9以上的版本。java

Effective Java, Third Edition

80. EXECUTORS, TASKS, STREAMS 優於線程

本書的初版包含一個簡單工做隊列的代碼[Bloch01,條目 49]。 此類容許客戶端將後臺線程的異步處理工做排入隊列。 當再也不須要工做隊列時,客戶端能夠調用一個方法,要求後臺線程在完成隊列中已有的任何工做後正常終止自身。 實現只不過是個玩具,但即使如此,它還須要一整頁精細,細緻的代碼,若是你沒有恰到好處的話,這種代碼很容易出現安全和活性失敗。 幸運的是,沒有理由再編寫這種代碼了。git

到本書第二版出版時,java.util.concurrent包已添加到Java中。 該包包含一個Executor Framework,它是一個靈活的基於接口的任務執行工具。 建立一個比本書初版更好的工做隊列只須要一行代碼:github

ExecutorService exec = Executors.newSingleThreadExecutor();

下面是如何提交一個可運行的(runnable)執行:小程序

exec.execute(runnable);

下面是如何告訴executor優雅地終止(若是作不到這一點,你的虛擬機極可能不會退出):緩存

exec.shutdown();

可使用執行器服務(executor service)作更多的事情。例如,能夠等待一個特定任務完成(條目 79中使用get方法, 319頁),能夠等待任何或所有任務完成的集合(使用invokeAny或invokeAll方法),也能夠等待執行者服務終止(使用awaitTermination方法),能夠在完成任務時逐個檢索任務結果(使用ExecutorCompletionService),能夠安排任務在特定時間運行或按期運行(使用ScheduledThreadPoolExecutor),等等。安全

若是但願多個線程處理來自隊列的請求,只需調用另外一個靜態工廠,該工廠建立一種稱爲線程池的不一樣類型的執行器服務。 能夠建立具備固定或可變數量線程的線程池。 java.util.concurrent.Executors類包含靜態工廠,它們提供了你須要的大多數執行程序。 可是,若是想要一些不同凡響的東西,能夠直接使用ThreadPoolExecutor類。 此類容許你配置線程池操做的幾乎每一個方面。服務器

爲特定應用程序選擇執行程序服務可能很棘手。 對於小程序或負載較輕的服務器,Executors.newCachedThreadPool一般是一個不錯的選擇,由於它不須要配置,一般「作正確的事情」。可是對於負載很重的生產服務器來講,緩存線程池不是一個好的選擇! 在緩存線程池中,提交的任務不會排隊,而是當即傳遞給線程執行。 若是沒有可用的線程,則建立一個新線程。 若是服務器負載太重以致於全部CPU都被充分利用而且更多任務到達時,則會建立更多線程,這隻會使事情變得更糟。 所以,在負載很重的生產服務器中,最好使用Executors.newFixedThreadPool,它提供具備固定線程數的池,或直接使用ThreadPoolExecutor類,以實現最大程度的控制。多線程

不只應該避免編寫本身的工做隊列,並且一般應該避免直接使用線程。 當直接使用Thread類時,線程既能夠做爲工做單元,也能夠做爲執行它的機制。 在executor framework中,工做單元和執行機制是分開的。 關鍵的抽象是工做單元,稱爲任務。 有兩種任務:Runnable及其近親Callable(相似於Runnable,除了它返回一個值而且能夠拋出任意異常)。 執行任務的通常機制是executor service。 若是從任務的角度來看,讓executor service爲你執行它們,能夠靈活地選擇適當的執行策略以知足你的需求,並在需求發生變化時更改策略。 本質上本質上,Executor Framework執行的功能與Collections Framework聚合(aggregation)功能是相同的。異步

在Java 7中,Executor Framework被擴展爲支持fork-join任務,這些任務由稱爲fork-join池的特殊executor service運行。 由ForkJoinTask實例表示的fork-join任務能夠拆分爲較小的子任務,而包含ForkJoinPool的線程不只處理這些任務,並且還「彼此」竊取「任務」以確保全部線程都保持忙碌,從而致使更高的任務 CPU利用率,更高的吞吐量和更低的延遲。 編寫和調優fork-join任務很棘手。 並行流(Parallel streams)(條目 48)是在fork-join池之上編寫的,假設它們適合當前的任務,那麼你能夠輕鬆地利用它們的性能優點。工具

對Executor Framework的完整處理超出了本書的範圍,但感興趣的讀者能夠參考 《Java Concurrency in Practice》一書[Goetz06]。

相關文章
相關標籤/搜索