1.為什麼要使用線程池
在Java中,如果每個請求到達就創建一個新線程,開銷是相當大的。在實際使用中,服務器在創建和銷毀線程上花費的時間和消耗的系統資源都相當大,甚至可能要比在處理實際的用戶請求的時間和資源要多的多。除了創建和銷毀線程的開銷之外,活動的線程也需要消耗系統資源。如果在一個jvm裡創建太多的線程,可能會使系統由於過度消耗內存或“切換過度”而導致系統資源不足。為了防止資源不足,服務器應用程序需要采取一些辦法來限制任何給定時刻處理的請求數目,盡可能減少創建和銷毀線程的次數,特別是一些資源耗費比較大的線程的創建和銷毀,盡量利用已有對象來進行服務,這就是“池化資源”技術產生的原因。
線程池主要用來解決線程生命周期開銷問題和資源不足問題。通過對多個任務重復使用線程,線程創建的開銷就被分攤到了多個任務上了,而且由於在請求到達時線程已經存在,所以消除了線程創建所帶來的延遲。這樣,就可以立即為請求服務,使用應用程序響應更快。另外,通過適當的調整線程中的線程數目可以防止出現資源不足的情況。
2.線程池的組成部分
一個比較簡單的線程池至少應包含線程池管理器、工作線程、任務列隊、任務接口等部分。其中線程池管理器的作用是創建、銷毀並管理線程池,將工作線程放入線程池中;工作線程是一個可以循環執行任務的線程,在沒有任務是進行等待;任務列隊的作用是提供一種緩沖機制,將沒有處理的任務放在任務列隊中;任務接口是每個任務必須實現的接口,主要用來規定任務的入口、任務執行完後的收尾工作、任務的執行狀態等,工作線程通過該接口調度任務的執行。
線程池管理器至少有下列功能:創建線程池,銷毀線程池,添加新任務。
工作線程是一個可以循環執行任務的線程,在沒有任務時將等待。
任務接口是為所有任務提供統一的接口,以便工作線程處理。任務接口主要規定了任務的入口,任務執行完後的收尾工作,任務的執行狀態等。
3.線程池適合應用的場合
當一個服務器接受到大量短小線程的請求時,使用線程池技術是非常合適的,它可以大大減少線程的創建和銷毀次數,提高服務器的工作效率。但是線程要求的運動時間比較長,即線程的運行時間比…….
以上信息來自如下文章:http://www.blogJava.Net/stevenjohn/archive/2011/12/12/366161.Html
一、Java自帶線程池
先看看Java自帶線程池的例子,開啟5個線程打印字符串List:
package com.luo.test; import Java.util.ArrayList; import Java.util.List; import Java.util.concurrent.ArrayBlockingQueue; import Java.util.concurrent.ThreadPoolExecutor; import Java.util.concurrent.TimeUnit; public class ThreadTest { public static void main(String[] args) { List<String> strList = new ArrayList<String>(); for (int i = 0; i < 100; i++) { strList.add("String" + i); } int threadNum = strList.size() < 5 ? strList.size() : 5; ThreadPoolExecutor executor = new ThreadPoolExecutor(2, threadNum, 300, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<Runnable>(3), new ThreadPoolExecutor.CallerRunsPolicy()); for (int i = 0; i < threadNum; i++) { executor.execute(new PrintStringThread(i,strList,threadNum)); } executor.shutdown(); } } class PrintStringThread implements Runnable { private int num; private List<String> strList; private int threadNum; public PrintStringThread(int num, List<String> strList, int threadNum) { this.num = num; this.strList = strList; this.threadNum = threadNum; } public void run() { int length = 0; for(String str : strList){ if (length % threadNum == num) { System.out.println("線程編號:" + num + ",字符串:" + str); } length ++; } } }
Java自帶線程池構造方法
ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue RejectedExecutionHandler handler) corePoolSize: 線程池維護線程的最少線程數,也是核心線程數,包括空閒線程 maximumPoolSize: 線程池維護線程的最大線程數 keepAliveTime: 線程池維護線程所允許的空閒時間 unit: 程池維護線程所允許的空閒時間的單位 workQueue: 線程池所使用的緩沖隊列 handler: 線程池對拒絕任務的處理策略 當一個任務通過execute(Runnable)方法欲添加到線程池時: 1、 如果此時線程池中的數量小於corePoolSize,即使線程池中的線程都處於空閒狀態,也要創建新的線程來處理被添加的任務。 2、 如果此時線程池中的數量等於 corePoolSize,但是緩沖隊列 workQueue未滿,那麼任務被放入緩沖隊列。 3、如果此時線程池中的數量大於corePoolSize,緩沖隊列workQueue滿,並且線程池中的數量小於maximumPoolSize,建新的線程來處理被添加的任務。 4、 如果此時線程池中的數量大於corePoolSize,緩沖隊列workQueue滿,並且線程池中的數量等於maximumPoolSize,那麼通過 handler所指定的策略來處理此任務。也就是:處理任務的優先級為:核心線程corePoolSize、任務隊列workQueue、最大線程 maximumPoolSize,如果三者都滿了,使用handler處理被拒絕的任務。 5、 當線程池中的線程數量大於 corePoolSize時,如果某線程空閒時間超過keepAliveTime,線程將被終止。這樣,線程池可以動態的調整池中的線程數。
分割線
事實上上面的例子代碼寫得有不足之處,如果你看出不足之處,說明你理解了線程池。否則可以多看幾遍哦。
二、Spring線程池配置
3.1、直接調用
ThreadPoolTaskExecutor poolTaskExecutor = new ThreadPoolTaskExecutor(); //線程池所使用的緩沖隊列 poolTaskExecutor.setQueueCapacity(200); //線程池維護線程的最少數量 poolTaskExecutor.setCorePoolSize(5); //線程池維護線程的最大數量 poolTaskExecutor.setMaxPoolSize(1000); //線程池維護線程所允許的空閒時間 poolTaskExecutor.setKeepAliveSeconds(30000); poolTaskExecutor.initialize();
3.2、通過配置文件
<bean id="poolTaskExecutor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor"> <!-- 核心線程數,默認為1 --> <property name="corePoolSize" value="5" /> <!-- 最大線程數,默認為Integer.MAX_VALUE --> <property name="maxPoolSize" value="50" /> <!-- 隊列最大長度,一般需要設置值>=notifyScheduledMainExecutor.maxNum;默認為Integer.MAX_VALUE --> <property name="queueCapacity" value="2000" /> <!-- 線程池維護線程所允許的空閒時間,默認為60s --> <property name="keepAliveSeconds" value="100" /> <!-- 線程池對拒絕任務(無線程可用)的處理策略,目前只支持AbortPolicy、CallerRunsPolicy;默認為後者 --> <property name="rejectedExecutionHandler"> <!-- AbortPolicy:直接拋出Java.util.concurrent.RejectedExecutionException異常 --> <!-- CallerRunsPolicy:主線程直接執行該任務,執行完之後嘗試添加下一個任務到線程池中,可以有效降低向線程池內添加任務的速度 --> <!-- DiscardOldestPolicy:拋棄舊的任務、暫不支持;會導致被丟棄的任務無法再次被執行 --> <!-- DiscardPolicy:拋棄當前任務、暫不支持;會導致被丟棄的任務無法再次被執行 --> <bean class="Java.util.concurrent.ThreadPoolExecutor$CallerRunsPolicy" /> </property> </bean>