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