Java 并發專題 : Executor詳細介紹 打造基于Executor的Web服務器
轉載標明出處:http://blog.csdn.net/lmj623565791/article/details/26938985
繼續并發,貌似并發的文章很少有人看啊~哈~
今天準備詳細介紹java并發包下的Executor,以及Java提供了很多靈活的且極其方便的線程池的創建。
嗯,那就慢慢說,大家肯定都學過Socket,JavaSe的時候寫聊天程序,游戲的服務器,以及Android程序自己需要提供服務器的,都會拿Socket來自己寫個:
最初我們的服務器可能寫成這樣:
1、單線程服務器
package com.zhy.concurrency.executors; import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; /** * 單線程Web服務器 * * @author zhy * */ public class SingleThreadWebServer { public static void main(String[] args) throws IOException { ServerSocket server = new ServerSocket(7711); while (true) { Socket client = server.accept(); handleReq(client); } } /** * 處理請求 * @param client */ private static void handleReq(Socket client) { } }
這樣的服務器代碼很簡單,理論上也是正確的,但是在實現的環境中卻很糟糕,因為它每次只能處理一個請求,如果每個請求的耗時過長,后面的請求會長時間等待或者超時錯誤。
于是乎,出現了下面這種寫法:
2、每個請求分配一個線程的服務器
package com.zhy.concurrency.executors; import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; /** * 為每個請求分配一個線程 * @author zhy * */ public class TaskPreThreadServer { public static void main(String[] args) throws IOException { ServerSocket server = new ServerSocket(8811); while (true) { final Socket client = server.accept(); new Thread() { public void run() { handleReq(client); }; }.start(); } } protected static void handleReq(Socket client) { } }
為每一個請求開辟一個線程,首先我得承認我也經常用這樣的寫法~但是我們還是要吐槽下這樣寫法的不足:
a、線程的生命周期的開銷還是相當高的,大量的線程的創建將消耗大量的計算機資源
b、可創建線程的數量存在一個限制值(這個值由平臺覺得,且受很多因素的制約),如果超過這個限制,可能會報OOM錯誤
c、在一定范圍內,增加線程可以提高系統吞吐量,但是超過了這個范圍,就物極必反了,只會降低程序的執行速度。
所以我們要繼續改進我們的服務器代碼,Executor提供了非常方便的方式:
3、基于Executor的服務器
package com.zhy.concurrency.executors; import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; import java.util.concurrent.Executor; import java.util.concurrent.Executors; /** * 基于Executor的服務器 * @author zhy * */ public class TaskExecutionServer { private static final int THREAD_COUNT = 100; private static final Executor exec = Executors .newFixedThreadPool(THREAD_COUNT); public static void main(String[] args) throws IOException { ServerSocket server = new ServerSocket(9911); while (true) { final Socket client = server.accept(); Runnable task = new Runnable() { @Override public void run() { handleReq(client); } }; exec.execute(task); } } protected static void handleReq(Socket client) { } }
創建一個固定長度的線程池,既解決了單線程的阻塞問題,也解決了無限創建線程帶來的內存消耗過多等問題。
4、Executors的API介紹
Java類庫提供了許多靜態方法來創建一個線程池:
a、newFixedThreadPool 創建一個固定長度的線程池,當到達線程最大數量時,線程池的規模將不再變化。
b、newCachedThreadPool 創建一個可緩存的線程池,如果當前線程池的規模超出了處理需求,將回收空的線程;當需求增加時,會增加線程數量;線程池規模無限制。
c、newSingleThreadPoolExecutor 創建一個單線程的Executor,確保任務對了,串行執行
d、newScheduledThreadPool 創建一個固定長度的線程池,而且以延遲或者定時的方式來執行,類似Timer;后面后單獨使用Blog介紹它與Timer區別
小結一下:在線程池中執行任務比為每個任務分配一個線程優勢更多,通過重用現有的線程而不是創建新線程,可以在處理多個請求時分攤線程創建和銷毀產生的巨大的開銷。當請求到達時,通常工作線程已經存在,提高了響應性;通過配置線程池的大小,可以創建足夠多的線程使CPU達到忙碌狀態,還可以防止線程太多耗盡計算機的資源。
好了,結束~歡迎大家留言~
來自: http://blog.csdn.net//lmj623565791/article/details/26938985