併發程序的構建 java
大多數的併發程序都是經過「任務執行」來構造的,任務一般是一些抽象且離散的工做單元。將業務邏輯抽象城一個個的任務,交給不一樣線程來併發執行。java中能夠經過Runnable來定義任務單元,經過Thread以獨立的線程執行。線程是比較寶貴的資源,須要合理的複用、管理、分配、執行。線程池管理線程使用的一個組件。安全
線程池 併發
jdk的線程池使用很是簡單,經過Executor的excute方法執行任務便可,至於線程如何管理取決於Executor的實現。Executor經過生產者-消費者模式,將任務和執行策略進行了解耦。spa
public interface Executor { void execute(Runnable command); }
jdk提供了ThreadPoolExecutor做爲Executor的線程池實現。該實現線程池中有幾個重要的概念:固定線程數(核心線程數)、最大線程數、隊列大小、線程存活時間。固定線程數建立後就不會消亡;隊列是超過固定線程執行能力的任務暫存於隊列;最大限線程數是超過固定線程及隊列能力的提供動態擴展線程能力,在使用完後超過指定存活時間沒有被使用就會被消亡。經過這幾個核心變量來建立不一樣的線程池。線程
執行流程:當提交任務給線程池時,首先會判斷當前線程個數是否小於核心線程池數,小於直接建立線程執行,不然將任務放入隊列;若是隊列也滿了,那麼就會看當前線程是否小於最大線程數,小於直接建立線程執行。線程池會不停從隊列中取任務,超過核心線程數的線程,在存活時間後,會自動回收。code
jdk提供了一些線程池和默認的配置,能夠經過Executors中的靜態工廠方法來建立線程池。對象
方法 | 說明 |
newFixedThreadPool | 建立一個固定長度的線程池,固定線程數n;最大線程數n;LinkedBlockingQueue隊列 |
newCachedThreadPool | 建立一個可擴展的線程池,固定線程數0;最大線程數Integer.MAX_VALUE;存活60秒;SynchronousQueue隊列 |
newSingleThreadExecutor | 建立一個單線程的線程池,固定線程數1;最大線程數1;LinkedBlockingQueue隊列 |
newScheduledThreadPool | 建立一個固定長度的線程池,而且以延時或定時執行 |
爲了解決執行服務的生命週期問題,Executor擴展了ExecutorService接口,而且提供了一些生命週期管理的方法。ExecutorService有3種狀態,運行、關閉、終止。在初始建立的時候,處於運行狀態;當調用shutdown後,再也不接受新任務,已提交的任務會執行完,此時處於關閉狀態;當任務執行完,則處於終止狀態。blog
shutdownNow與shutdown的不一樣在於,shutdownNow不會等待已提交的任務執行完,而是當即嘗試中止執行的任務和在隊列中的任務,而且返回等待中的任務。接口
awaitTermination會執行shutdown而且阻塞,直處處於終止狀態。生命週期
Callable與Future
Executor使用Runnable做爲其任務表示時,有一個缺陷就是Runnable沒有返回值而且不能拋出受檢查的異常。這使得咱們須要經過一些共享資源來同步線程執行結果。
Callable多是一個對任務更全面的定義。
public interface Callable<V> { V call() throws Exception; }
ExecutorService一樣擴展了獲取返回值和執行異常的能力而且提供了取消任務的能力, <T> Future<T> submit(Callable<T> task)方法提交一個任務,並返回一個Future。本質上Future的實現就是一個共享對象,線程執行完本身的任務後會將結果設置在Future的實現中,而且進行了併發控制,實現線程安全。
Future的get方法會一直阻塞直到任務完成,返回結果;若是執行拋出異常,則會將異常封裝成ExecutionException從新拋出;若是任務被取消,則會拋出CancellationException;cancel(boolean mayInterruptIfRunning)嘗試取消已提交的任務,若是任務還未執行,則任務將被取消並返回true,若是任務已經執行,根據mayInterruptIfRunning嘗試中斷。已經執行或者已經取消的任務將返回false。
此外ExecutorService提供了批量執行的方法,invokeAll提交一個Callable集合,並按順序返回Future集合。invokeAny提交一個Callable集合,任意一個任務執行完成即返回。
線程池的管理
線程池的各項配置依賴與任務自己(任務運行時長,cpu使用率等)以及任務依賴的各項資源(cpu、內存、依賴任務等),(從線程的角度來看,線程仍是串行執行任務的),最終的目的還說讓cpu達到最高的利用率,而且減小任務切換帶來的額外損耗。