早期的應用程序大可能是單線程串行執行的,雖然程序的任務邊界清晰有序,可是執行的效率卻很低,尤爲是執行花費時間較長的操做,會致使大量的等待和堆積。爲了提升程序的執行效率和吞吐量,咱們很天然的會想到多線程,即爲每一個任務都新建一個獨立的線程,這樣就極大地提升了程序的執行效率。但事實上多線程也會帶來不少問題。好比大量的建立線程,這自己就會消耗不少的資源,尤爲是內存,當建立的線程數量超過服務器可以承受的極限時,內存溢出是在所不免的;好比還有其餘穩定性問題,以及多線程形成的程序調用和管理的混亂。因此,綜上所述,咱們須要一個介於二者之間的工具,既能夠建立大量的線程來提升程序的併發性和吞吐量,同時又能夠有序的管理這些線程,可以可控。而Executor接口和線程池技術就是在這種背景下應運而生的。下面分別將這兩種經常使用的併發技術作以介紹和總結。java
java.util.coucurrent包下面爲咱們提供了豐富的併發工具,Executor和ExecutorService接口就是其中比較重要的兩個。緩存
public interface Executor{ void execute(Runnable command); }
以上是Executor接口的代碼,能夠看出這個藉口很是簡單,就只有一個execute()方法。但他卻爲強大的異步任務執行提供了基礎,它支持不一樣類型的任務執行策略。Executor框架基於生產者消費者模型,它提供了一個標準的方法將任務的提交和任務的執行過程解耦,並用Runnable來表示任務。下面來看一個基於Executor的簡單服務器實現:服務器
public class ExecutorWebServer { private static final int LIMIT = 50; private static final Executor exe = Executors.newFixedThreadPool(LIMIT); public static void main(String[] args) throws IOException { // TODO Auto-generated method stub ServerSocket server = new ServerSocket(80); while(true){ Socket socket = server.accept(); Runnable task = new Runnable(){ public void run(){ doSomeThing(socket); } }; exe.execute(task); } } }
經過上面列子咱們就很好的把任務的提交和任務的執行分開來,這就是Executor框架最大的優點。多線程
上面咱們講了如何建立一個Executor,可是並無講如何關閉它。既然Executor是爲應用程序服務的,於是他們應該是可關閉的,並將關閉操做中受影響的任務狀態反饋給應用程序。爲了解決執行服務的生命週期問題,ExecutorService擴展了Executor接口,增長了管理生命週期的方法:併發
public interface ExecutorService Extends Executor{ void shutdown(); boolean isShutDown(); boolean isTerminated(); …… }
ExecutorService的生命週期分爲三種,運行,關閉和已終止。下面看這個簡單的實例:框架
class NetworkService implements Runnable { private final ServerSocket serverSocket; private final ExecutorService pool; public NetworkService(int port, int poolSize) throws IOException { serverSocket = new ServerSocket(port); pool = Executors.newFixedThreadPool(poolSize); } public void run() { // run the service try { for (;;) { pool.execute(new Handler(serverSocket.accept())); } } catch (IOException ex) { pool.shutdown(); } } } class Handler implements Runnable { private final Socket socket; Handler(Socket socket) { this.socket = socket; } public void run() { // read and service request on socket } }
能夠經過Executors類的幾個靜態方法來建立線程池。包含如下幾類線程池:
newFixedThreadPool建立固定長度的線程池。每提交一個任務時就新建一個線程,直到達到線程池的最大數量。異步
newCachedThreadPool建立一個可緩存的線程池,線程的數量不受限制,可是在之前線程可用時將重用他們。socket
newSingleThreadExecutor建立一個使用單個 worker 線程的 ,以無界隊列方式來運行該線程。函數
newScheduleThreadPool建立一個線程池,它可安排在給定延遲後運行命令或者按期地執行。相似於一個定時器。工具
線程池的理想大小取決於被提交任務的類型以及所部署系統的特性。在代碼中最好不要固定線程池的大小,而要經過某種靈活機制來配置。線程池大小的配置只要避免「過大」和」太小「兩種極端狀況便可。線程池過大會致使大量的線程競爭CPU和內存,最終致使資源耗盡;線程池太小時又會致使資源浪費,因此設置一個合適的線程池大小很是重要。一般有一個簡單的設置線程池大小的公式供咱們參考使用:
N(Threads) = N(cpu) * U(cpu) * (1 + w/c)
N(cpu)表明cpu的個數,U(cpu)表明cpu利用率, w/c表示等待時間與計算時間的比值
ThreadPoolExecutor爲Executor和ExecutorService接口提供了基本實現。下面咱們來看一下ThreadPoolExecutor類的構造函數:該類一共有四個構造函數,其中最基礎的一個構造函數以下:
ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue)
corePoolSize表示的是核心池的大小,第二個參數表示線程池最大的線程數,第三個參數表示存活時間,第四個參數表示給定單元粒度的時間段,第五個參數表示的是工做隊列。
ThreadPoolExecutor是能夠擴展的,它提供了幾個能夠在子類中改寫的方法:beforeExecutor,afterExecutor,terminated,這些方法可用於擴展ThreadPoolExecutor的行爲。