1. 程式人生 > >java執行緒池技術,經典易懂

java執行緒池技術,經典易懂

1.為什麼要使用執行緒池

在java中,如果每個請求到達就建立一個新執行緒,開銷是相當大的。在實際使用中,伺服器在建立和銷燬執行緒上花費的時間和消耗的系統資源都相當大,甚至可能要比在處理實際的使用者請求的時間和資源要多的多。除了建立和銷燬執行緒的開銷之外,活動的執行緒也需要消耗系統資源。如果在一個jvm裡建立太多的執行緒,可能會使系統由於過度消耗記憶體或“切換過度”而導致系統資源不足。為了防止資源不足,伺服器應用程式需要採取一些辦法來限制任何給定時刻處理的請求數目,儘可能減少建立和銷燬執行緒的次數,特別是一些資源耗費比較大的執行緒的建立和銷燬,儘量利  用已有物件來進行服務,這就是“池化資源”技術產生的原因。

執行緒池主要用來解決執行緒生命週期開銷問題和資源不足問題。通過對多個任務重複使用執行緒,執行緒建立的開銷就被分攤到了多個任務上了,而且由於在請求到達時執行緒已經存在,所以消除了執行緒建立所帶來的延遲。這樣,就可以立即為請求服務,使用應用程式響應更快。另外,通過適當的調整執行緒中的執行緒數目可以防止出現資源不足的情況。

2.執行緒池的組成部分

一個比較簡單的執行緒池至少應包含執行緒池管理器、工作執行緒、任務列隊、任務介面等部分。其中執行緒池管理器的作用是建立、銷燬並管理執行緒池,將工作執行緒放入執行緒池中;工作執行緒是一個可以迴圈執行任務的執行緒,在沒有任務是進行等待;任務列隊的作用是提供一種緩衝機制,將沒有處理的任務放在任務列隊中;任務介面是每個任務必須實現的介面,主要用來規定任務的入口、任務執行完後的收尾工作、任務的執行狀態等,工作執行緒通過該介面排程任務的執行。

執行緒池管理器至少有下列功能:建立執行緒池,銷燬執行緒池,新增新任務。

工作執行緒是一個可以迴圈執行任務的執行緒,在沒有任務時將等待。

任務介面是為所有任務提供統一的介面,以便工作執行緒處理。任務介面主要規定了任務的入口,任務執行完後的收尾工作,任務的執行狀態等。

3.執行緒池適合應用的場合

當一個伺服器接受到大量短小執行緒的請求時,使用執行緒池技術是非常合適的,它可以大大減少執行緒的建立和銷燬次數,提高伺服器的工作效率。但是執行緒要求的運動時間比較長,即執行緒的執行時間比…….

一、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>