線程池的簡單運用ThreadPoolExecutor

線程池解決的問題。

  1. 提升性能:它們通常在執行大量異步任務時,由於減少了每個任務的調用開銷,並且它們提供了一種限制和管理資源(包括線程)的方法,使得性能提升明顯;
  2. 統計信息:每個ThreadPoolExecutor保持一些基本的統計信息,例如完成的任務數量。

序號

名稱

類型

含義

1

corePoolSize

int

核心線程池大

2

maximumPoolSize

int

最大線程池大小

3

keepAliveTime

long

線程最大空閒時

4

unit

TimeUnit

時間單

5

workQueue

BlockingQueue<Runnable>

線程等待隊

6

threadFactory

ThreadFactory

線程創建工

7

handler

RejectedExecutionHandler

拒絕策

java中有哪些線程池

Java通過Executors提供四種線程池,分別爲:

newCachedThreadPool——創建一個可緩存線程池,如果線程池長度超過處理需要,可靈活回收空閒線程,若無可回收,則新建線程。newFixedThreadPool——創建一個定長線程池,可控制線程最大併發數,超出的線程會在隊列中等待。newScheduledThreadPool——創建一個定長線程池,支持定時及週期性任務執行。

newSingleThreadExecutor——創建一個單線程化的線程池,它只會用唯一的工作線程來執行任務,保證所有任務按照指定順序(FIFO, LIFO, 優先級)執行。

在自定義線程池時,請參考以下指南:

一、Core and maximum pool sizes 核心和最大線程池數量

參數 翻譯
corePoolSize 核心線程池數量
maximumPoolSize 最大線程池數量

線程池執行器將會根據corePoolSize和maximumPoolSize自動地調整線程池大小。

當在execute(Runnable)方法中提交新任務並且少於corePoolSize線程正在運行時,即使其他工作線程處於空閒狀態,也會創建一個新線程來處理該請求。 如果有多於corePoolSize但小於maximumPoolSize線程正在運行,則僅當隊列已滿時纔會創建新線程。 通過設置corePoolSize和maximumPoolSize相同,您可以創建一個固定大小的線程池。 通過將maximumPoolSize設置爲基本上無界的值,例如Integer.MAX_VALUE,您可以允許池容納任意數量的併發任務。 通常,核心和最大池大小僅在構建時設置,但也可以使用setCorePoolSizesetMaximumPoolSize進行動態更改。

二、prestartCoreThread 核心線程預啓動
在默認情況下,只有當新任務到達時,纔開始創建和啓動核心線程,但是我們可以使用 prestartCoreThread()prestartAllCoreThreads() 方法動態調整。
如果使用非空隊列構建池,則可能需要預先啓動線程。

方法 作用
prestartCoreThread() 創一個空閒任務線程等待任務的到達
prestartAllCoreThreads() 創建核心線程池數量的空閒任務線程等待任務的到達

三、ThreadFactory 線程工廠

新線程使用ThreadFactory創建。 如果未另行指定,則使用Executors.defaultThreadFactory默認工廠,使其全部位於同一個ThreadGroup中,並且具有相同的NORM_PRIORITY優先級和非守護進程狀態。

通過提供不同的ThreadFactory,您可以更改線程的名稱,線程組,優先級,守護進程狀態等。如果ThreadCactory在通過從newThread返回null詢問時未能創建線程,則執行程序將繼續,但可能無法執行任何任務。

線程應該有modifyThread權限。 如果工作線程或使用該池的其他線程不具備此權限,則服務可能會降級:配置更改可能無法及時生效,並且關閉池可能會保持可終止但尚未完成的狀態。

四、Keep-alive times 線程存活時間

如果線程池當前擁有超過corePoolSize的線程,那麼多餘的線程在空閒時間超過keepAliveTime時會被終止 ( 請參閱getKeepAliveTime(TimeUnit) )。這提供了一種在不積極使用線程池時減少資源消耗的方法。

如果池在以後變得更加活躍,則應構建新線程。 也可以使用方法setKeepAliveTime(long,TimeUnit)進行動態調整。

防止空閒線程在關閉之前終止,可以使用如下方法:

setKeepAliveTime(Long.MAX_VALUE,TimeUnit.NANOSECONDS);

默認情況下,keep-alive策略僅適用於存在超過corePoolSize線程的情況。 但是,只要keepAliveTime值不爲零,方法allowCoreThreadTimeOut(boolean)也可用於將此超時策略應用於核心線程

五、Queuing 隊列

BlockingQueu用於存放提交的任務,隊列的實際容量與線程池大小相關聯。

  • 如果當前線程池任務線程數量小於核心線程池數量,執行器總是優先創建一個任務線程,而不是從線程隊列中取一個空閒線程。

  • 如果當前線程池任務線程數量大於核心線程池數量,執行器總是優先從線程隊列中取一個空閒線程,而不是創建一個任務線程。

  • 如果當前線程池任務線程數量大於核心線程池數量,且隊列中無空閒任務線程,將會創建一個任務線程,直到超出maximumPoolSize,如果超時maximumPoolSize,則任務將會被拒絕。

這個過程參考[線程任務處理流程圖.png]

主要有三種隊列策略:

  1. Direct handoffs 直接握手隊列
    Direct handoffs 的一個很好的默認選擇是 SynchronousQueue,它將任務交給線程而不需要保留。這裏,如果沒有線程立即可用來運行它,那麼排隊任務的嘗試將失敗,因此將構建新的線程。
    此策略在處理可能具有內部依賴關係的請求集時避免鎖定。Direct handoffs 通常需要無限制的maximumPoolSizes來避免拒絕新提交的任務。 但得注意,當任務持續以平均提交速度大餘平均處理速度時,會導致線程數量會無限增長問題。

  2. Unbounded queues 無界隊列
    當所有corePoolSize線程繁忙時,使用無界隊列(例如,沒有預定義容量的LinkedBlockingQueue)將導致新任務在隊列中等待,從而導致maximumPoolSize的值沒有任何作用。當每個任務互不影響,完全獨立於其他任務時,這可能是合適的; 例如,在網頁服務器中, 這種隊列方式可以用於平滑瞬時大量請求。但得注意,當任務持續以平均提交速度大餘平均處理速度時,會導致隊列無限增長問題。

  3. Bounded queues 有界隊列
    一個有界的隊列(例如,一個ArrayBlockingQueue)和有限的maximumPoolSizes配置有助於防止資源耗盡,但是難以控制。隊列大小和maximumPoolSizes需要 相互權衡

  • 使用大隊列和較小的maximumPoolSizes可以最大限度地減少CPU使用率,操作系統資源和上下文切換開銷,但會導致人爲的低吞吐量。如果任務經常被阻塞(比如I/O限制),那麼系統可以調度比我們允許的更多的線程。
  • 使用小隊列通常需要較大的maximumPoolSizes,這會使CPU更繁忙,但可能會遇到不可接受的調度開銷,這也會降低吞吐量。
    這裏主要爲了說明有界隊列大小和maximumPoolSizes的大小控制,若何降低資源消耗的同時,提高吞吐量

六、Rejected tasks 拒絕任務
拒絕任務有兩種情況:1. 線程池已經被關閉;2. 任務隊列已滿且maximumPoolSizes已滿;
無論哪種情況,都會調用RejectedExecutionHandler的rejectedExecution方法。預定義了四種處理策略:

  1. AbortPolicy:默認測策略,拋出RejectedExecutionException運行時異常;
  2. CallerRunsPolicy:這提供了一個簡單的反饋控制機制,可以減慢提交新任務的速度;
  3. DiscardPolicy:直接丟棄新提交的任務;
  4. DiscardOldestPolicy:如果執行器沒有關閉,隊列頭的任務將會被丟棄,然後執行器重新嘗試執行任務(如果失敗,則重複這一過程);
    我們可以自己定義RejectedExecutionHandler,以適應特殊的容量和隊列策略場景中。

七、Hook methods 鉤子方法
ThreadPoolExecutor爲提供了每個任務執行前後提供了鉤子方法,重寫beforeExecute(Thread,Runnable)afterExecute(Runnable,Throwable)方法來操縱執行環境; 例如,重新初始化ThreadLocals,收集統計信息或記錄日誌等。此外,terminated()在Executor完全終止後需要完成後會被調用,可以重寫此方法,以執行任殊處理。
注意:如果hook或回調方法拋出異常,內部的任務線程將會失敗並結束。

八、Queue maintenance 維護隊列
getQueue()方法可以訪問任務隊列,一般用於監控和調試。絕不建議將這個方法用於其他目的。當在大量的隊列任務被取消時,remove()purge()方法可用於回收空間。

九、Finalization 關閉

如果程序中不在持有線程池的引用,並且線程池中沒有線程時,線程池將會自動關閉。如果您希望確保即使用戶忘記調用 shutdown()方法也可以回收未引用的線程池,使未使用線程最終死亡。那麼必須通過設置適當的 keep-alive times 並設置allowCoreThreadTimeOut(boolean) 或者 使 corePoolSize下限爲0 。
一般情況下,線程池啓動後建議手動調用shutdown()關閉。

 

 

 

private ThreadPoolExecutor threadPoolExecutor;



@PostConstruct
private void init() {
    threadPoolExecutor = new ThreadPoolExecutor(commonConfig.getCorePoolSize(),
            commonConfig.getMaximumPoolSize(),
            commonConfig.getKeepAliveTime(),
            TimeUnit.SECONDS,
            new LinkedBlockingDeque<>(commonConfig.getCorePoolSize() * 20),
            new ThreadPoolExecutor.AbortPolicy());
}


void updateMapppingLastTransactionTimestamp(TokenMapping tokenMapping) {
    threadPoolExecutor.execute(() -> {
        try {
            tokenMappingIntegration.updateMapppingLastTransactionTimestamp(tokenMapping);
        } catch (Exception e) {
            LOG.error(SplunkMessage.allocate().add(ACTION, "update last transaction timestamp")
                    .add(CRYPTO_TOKEN, CommonUtil.maskAccountNumber(tokenMapping.getToken()))
                    .add("result", false)
                    .add(EXCEPTION, e));
        }
    });



https://www.jianshu.com/p/f030aa5d7a28   線程池
自定義線程池
package com.mastercard.panmapping.tokenmapping.controller.common;

import java.io.IOException;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * <p>Descriptions...
 * <p>Created by Diamon.Cheng on 4/4/2019.
 */
public class ThreadTest {

    public static void main(String[] args) throws InterruptedException, IOException {
        int corePoolSize = 2; //核心線程池大小
        int maximumPoolSize = 4;//最大線程池大小
        long keepAliveTime = 10;//線程最大空閒時間
        TimeUnit unit = TimeUnit.SECONDS; //時間單位
        //等待線程數太少
        BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(2); //線程等待隊列
        ThreadFactory threadFactory = new NameTreadFactory(); //線程創建工廠
        RejectedExecutionHandler handler = new MyIgnorePolicy();//拒絕策略
        ThreadPoolExecutor executor = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, unit,
                workQueue, threadFactory, handler);
        executor.prestartAllCoreThreads(); // 預啓動所有核心線程

        for (int i = 1; i <= 10; i++) {
            MyTask task = new MyTask(String.valueOf(i));
            executor.execute(task);
        }

       // System.in.read(); //阻塞主線程
    }

    //線程創建工廠
    static class NameTreadFactory implements ThreadFactory {

        private final AtomicInteger mThreadNum = new AtomicInteger(1);

        @Override
        public Thread newThread(Runnable r) {
            Thread t = new Thread(r, "my-thread-" + mThreadNum.getAndIncrement());
            System.out.println(t.getName() + " has been created");
            return t;
        }
    }
    //拒絕策略
    public static class MyIgnorePolicy implements RejectedExecutionHandler {

        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
            doLog(r, e);
        }

        private void doLog(Runnable r, ThreadPoolExecutor e) {
            // 可做日誌記錄等
            System.err.println( r.toString() + " 拒絕");
//          System.out.println("completedTaskCount: " + e.getCompletedTaskCount());
        }
    }
    //創建執行任務
    static class MyTask implements Runnable {
        private String name;

        public MyTask(String name) {
            this.name = name;
        }

        @Override
        public void run() {
            try {
                System.out.println(this.toString() + " 正在運行!");
                Thread.sleep(3000); //讓任務執行慢點
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        public String getName() {
            return name;
        }

        @Override
        public String toString() {
            return "MyTask [name=" + name + "]";
        }
    }
}

運行結果

MyTask [name=5] 拒絕
MyTask [name=6] 拒絕
MyTask [name=7] 拒絕
MyTask [name=8] 拒絕
MyTask [name=9] 拒絕
MyTask [name=10] 拒絕
my-thread-1 has been created
my-thread-2 has been created
my-thread-3 has been created
my-thread-4 has been created
MyTask [name=1] 正在運行!
MyTask [name=2] 正在運行!
MyTask [name=3] 正在運行!
MyTask [name=4] 正在運行!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章