當前位置:首頁 » 存儲配置 » 一個線程池需要配置哪些參數

一個線程池需要配置哪些參數

發布時間: 2022-05-30 01:34:19

㈠ 什麼是java線程池

多線程是為了能夠讓計算機資源合理的分配,對於處理不同的任務創建不同的線程進行處理,但是計算機創建一個線程或者銷毀一個線程所花費的也是比較昂貴的,有時候需要同時處理的事情比較多,就需要我們頻繁的進行線程的創建和銷毀,這樣花費的時間也是比較多的。為了解決這一問題,我們就可以引用線程池的概念。
所謂線程池就是將線程集中管理起來,當需要線程的時候,可以從線程池中獲取空閑的線程,這樣可以減少線程的頻繁創建與銷毀,節省很大的時間和減少很多不必要的操作。
在java中提供了ThreadPoolExecutor類來進行線程的管理,這個類繼承於AbstractExecutorService,而AbstractExecutorService實現了ExecutorService介面,我們可以使用ThreadPoolExecutor來進行線程池的創建。
在ThreadPoolExecutor的構造方法中,有多個參數,可以配置不同的參數來進行優化。這個類的源碼構造方法為:
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler)其中每個參數代表的意義分別為
corePoolSize : 線程池中的核心線程數量,當線程池中當前的線程數小於這個配置的時候,如果有一個新的任務到來,即使線程池中還存在空閑狀態的線程,程序也會繼續創建一個新的線程放進線程池當中
maximumPoolSize: 線程池中的線程最大數量
keepAliveTime:當線程池中的線程數量大於配置的核心線程數量(corePoolSize)的時候,如果當前有空閑的線程,則當這個空閑線程可以存在的時間,如果在keepAliveTime這個時間點內沒有新的任務使用這個線程,那麼這個線程將會結束,核心線程不會結束,但是如果配置了allowCoreThreadTimeOut = true,則當空閑時間超過keepAliveTime之後,線程也會被結束調,默認allowCoreThreadTimeOut = false,即表示默認情況下,核心線程會一直存在於線程池當中。
unit : 空閑線程保持連接時間(keepAliveTime)的時間單位
workQueue:阻塞的任務隊列,用來保存等待需要執行的任務。
threadFactory :線程工廠,可以根據自己的需求去創建線程的對象,設置線程的名稱,優先順序等屬性信息。
handler:當線程池中存在的線程數超過設置的最大值之後,新的任務就會被拒絕,可以自己定義一個拒絕的策略,當新任務被拒絕之後,就會使用hander方法進行處理。
在java中也提供了Executors工具類,在這個工具類中提供了多個創建線程池的靜態方法,其中包含newCachedThreadPool、newFixedThreadPool、newScheledThreadPool、newSingleThreadExecutor等。但是他們每個方法都是創建了ThreadPoolExecutor對象,不同的是,每個對象的初始 參數值不一樣;

㈡ 線程池的參數有哪些

在Java編碼的過程中,我們經常會創建一個線程來提高程序的執行效率,雖然這樣實現起來很方便,但是會有一個問題:如果並發的線程數多,並且每個線程都是執行一個時間很短的任務就結束了,這樣會造成頻繁的創建和銷毀線程從而導致降低系統的效率。
那麼問題來了,有沒有辦法可用復用創建好的線程呢,也就是線程執行完一個任務後,不被銷毀,繼續執行其他的任務?

在Java可以通過線程池來實現這樣的效果。
下面從三個方面和大家一起來探討一下Java線程池相關的內容。
1.Java中的ThreadPoolExecutor類。
2.Java中4種線程池的使用。
3.Java線程池常用參數如何設置。

一、Java中的ThreadPoolExecutor類

A.ThreadPoolExecutor的重要參數

1.corePoolSize:核心線程數
核心線程會一直存活,及時沒有任務需要執行。
當線程數小於核心線程數時,即使有線程空閑,線程池也會優先創建新線程處理。
設置allowCoreThreadTimeout=true(默認false)時,核心線程會超時關閉。
2.queueCapacity:任務隊列容量(阻塞隊列)
當核心線程數達到最大時,新任務會放在隊列中排隊等待執行。
3.maxPoolSize:最大線程數
當線程數>=corePoolSize,且任務隊列已滿時。線程池會創建新線程來處理任務。
當線程數=maxPoolSize,且任務隊列已滿時,線程池會拒絕處理任務而拋出異常。
4.keepAliveTime:線程空閑時間
當線程空閑時間達到keepAliveTime時,線程會退出,直到線程數量=corePoolSize。
如果allowCoreThreadTimeout=true,則會直到線程數量=0。
5.allowCoreThreadTimeout:允許核心線程超時
6.rejectedExecutionHandler:任務拒絕處理器。

B.ThreadPoolExecutor執行過程

1.當線程數小於核心線程數時,創建線程。

2.當線程數大於等於核心線程數,且任務隊列未滿時,將任務放入任務隊列。

3.當線程數大於等於核心線程數,且任務隊列已滿。(1)若線程數小於最大線程數,創建線程。(2)若線程數等於最大線程數,拋出異常,拒絕任務。

二、Java中4種線程池
Java通過Executors提供四種線程池,分別為:
newCachedThreadPool創建一個可緩存線程池,如果線程池長度超過處理需要,可靈活回收空閑線程,若無可回收,則新建線程。
newFixedThreadPool 創建一個定長線程池,可控制線程最大並發數,超出的線程會在隊列中等待。
newScheledThreadPool 創建一個定長線程池,支持定時及周期性任務執行。
newSingleThreadExecutor 創建一個單線程化的線程池,它只會用唯一的工作線程來執行任務,保證所有任務按照指定順序(FIFO, LIFO, 優先順序)執行。

㈢ 線程池,threadpool有哪些重要 的參數

關於Java線程池的參數設置。線程池是Java多線程里開發里的重要內容,使用難度不大

㈣ 線程池有幾種實現方式,線程池的七大參數有哪些

  • 一:創建大小不固定的線程池

  • 二:創建固定數量線程的線程池

  • 三:創建單線程的線程池

  • 四:創建定時線程

  • 1.創建大小不固定的線程池

    [java]view plain

  • packagecom.peace.pms.Test;

  • importjava.util.concurrent.ExecutorService;

  • importjava.util.concurrent.Executors;

  • /**

  • *@Author:cxx

  • *@Date:2018/3/317:16

  • */

  • publicclassThreadPoolDemo{

  • {

  • @Override

  • publicvoidrun(){

  • for(inti=0;i<10;i++){

  • System.out.println(Thread.currentThread().getName()+":"+i);

  • }

  • }

㈤ 啥時候會使用線程池

編者註:Java中的線程池是運用場景最多的並發組件,幾乎所有需要非同步或並發執行任務的程序都可以使用線程池。

在開發過程中,合理地使用線程池能夠帶來至少以下幾個好處。

降低資源消耗:通過重復利用已創建的線程降低線程創建和銷毀造成的消耗。
提高響應速度:當任務到達時,任務可以不需要等到線程創建就能立即執行。
提高線程的可管理性:線程是稀缺資源,如果無限制地創建,不僅會消耗系統資源,還會降低系統的穩定性,使用線程池可以進行統一分配、調優和監控。但是,要做到合理利用線程池,必須了解其實現原理。
代碼解耦:比如生產者消費者模式。
線程池實現原理
當提交一個新任務到線程池時,線程池的處理流程如下:

如果當前運行的線程少於corePoolSize,則創建新線程來執行任務(注意,執行這一步驟需要獲取全局鎖)。
如果運行的線程等於或多於corePoolSize,則將任務加入BlockingQueue。
如果無法將任務加入BlockingQueue(隊列已滿),則創建新的線程來處理任務(注意,執行這一步驟也需要獲取全局鎖)。
如果創建新線程將使當前運行的線程數超出maximumPoolSize,該任務將被拒絕,並調用相應的拒絕策略來處理(RejectedExecutionHandler.rejectedExecution()方法,線程池默認的飽和策略是AbortPolicy,也就是拋異常)。
ThreadPoolExecutor採取上述步驟的總體設計思路,是為了在執行execute()方法時,盡可能地避免獲取全局鎖(那將會是一個嚴重的可伸縮瓶頸)。在ThreadPoolExecutor完成預熱之後(當前運行的線程數大於等於corePoolSize),幾乎所有的execute()方法調用都是執行步驟2,而步驟2不需要獲取全局鎖。

線程池任務 拒絕策略包括 拋異常、直接丟棄、丟棄隊列中最老的任務、將任務分發給調用線程處理。

線程池的創建:通過ThreadPoolExecutor來創建一個線程池。

new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, timeUnit, runnableTaskQueue, handler);
創建一個線程池時需要輸入以下幾個參數:

corePoolSize(線程池的基本大小):當提交一個任務到線程池時,線程池會創建一個線程來執行任務,即使其他空閑的基本線程能夠執行新任務也會創建線程,等到線程池的線程數等於線程池基本大小時就不再創建。如果調用了線程池的prestartAllCoreThreads()方法,線程池會提前創建並啟動所有基本線程。
maximumPoolSize(線程池最大數量):線程池允許創建的最大線程數。如果隊列滿了,並且已創建的線程數小於最大線程數,則線程池會再創建新的線程執行任務。值得注意的是,如果使用了無界的任務隊列這個參數就沒什麼效果。
keepAliveTime(線程活動保持時間):線程池的工作線程空閑後,保持存活的時間。所以,如果任務很多,並且每個任務執行的時間比較短,可以調大時間,提高線程的利用率。
TimeUnit(線程活動保持時間的單位):可選的單位有天(DAYS)、小時(HOURS)、分鍾(MINUTES)、毫秒(MILLISECONDS)、微秒(MICROSECONDS,千分之一毫秒)和納秒(NANOSECONDS,千分之一微秒)。
runnableTaskQueue(任務隊列):用於保存等待執行的任務的阻塞隊列。可以選擇以下幾個阻塞隊列。
- ArrayBlockingQueue:是一個基於數組結構的有界阻塞隊列,此隊列按FIFO(先進先出)原則對元素進行排序。
LinkedBlockingQueue:一個基於鏈表結構的阻塞隊列,此隊列按FIFO排序元素,吞吐量通常要高於ArrayBlockingQueue。靜態工廠方法Executors.newFixedThreadPool()使用了這個隊列。
SynchronousQueue:一個不存儲元素的阻塞隊列。每個插入操作必須等到另一個線程調用移除操作,否則插入操作一直處於阻塞狀態,吞吐量通常要高於LinkedBlockingQueue,靜態工廠方法Executors.newCachedThreadPool使用了這個隊列。
PriorityBlockingQueue:一個具有優先順序的無界阻塞隊列。
線程的狀態
在HotSpot VM線程模型中,Java線程被一對一映射到本地系統線程,Java線程啟動時會創建一個本地系統線程;當Java線程終止時,這個本地系統線程也會被回收。操作系統調度所有線程並把它們分配給可用的CPU。

thread運行周期中,有以下6種狀態,在 java.lang.Thread.State 中有詳細定義和說明:

// Thread類
public enum State {
/**
* 剛創建尚未運行
*/
NEW,

/**
* 可運行狀態,該狀態表示正在JVM中處於運行狀態,不過有可能是在等待其他資源,比如CPU時間片,IO等待
*/
RUNNABLE,

/**
* 阻塞狀態表示等待monitor鎖(阻塞在等待monitor鎖或者在調用Object.wait方法後重新進入synchronized塊時阻塞)
*/
BLOCKED,

/**
* 等待狀態,發生在調用Object.wait、Thread.join (with no timeout)、LockSupport.park
* 表示當前線程在等待另一個線程執行某種動作,比如Object.notify()、Object.notifyAll(),Thread.join表示等待線程執行完成
*/
WAITING,

/**
* 超時等待,發生在調用Thread.sleep、Object.wait、Thread.join (in timeout)、LockSupport.parkNanos、LockSupport.parkUntil
*/
TIMED_WAITING,

/**
*線程已執行完成,終止狀態
*/
TERMINATED;
}
線程池操作
向線程池提交任務,可以使用兩個方法向線程池提交任務,分別為execute()和submit()方法。execute()方法用於提交不需要返回值的任務,所以無法判斷任務是否被線程池執行成功。通過以下代碼可知execute()方法輸入的任務是一個Runnable類的實例。

threadsPool.execute(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
}
});
submit()方法用於提交需要返回值的任務。線程池會返回一個future類型的對象,通過這個future對象可以判斷任務是否執行成功,通過future的get()方法來獲取返回值,future的get()方法會阻塞當前線程直到任務完成,而使用get(long timeout,TimeUnit unit)方法則會阻塞當前線程一段時間後立即返回,這時候有可能任務還沒有執行完。

Future<Object> future = executor.submit(harReturnValuetask);
try {
Object s = future.get();
} catch (InterruptedException e) {
// 處理中斷異常
} catch (ExecutionException e) {
// 處理無法執行任務異常
} finally {
// 關閉線程池
executor.shutdown();
}
合理配置線程池
要想合理配置線程池,必須先分析任務的特點,可以從以下幾個角度分析:

任務的性質:CPU密集型任務、IO密集型任務和混合型任務。
任務的優先順序:高、中和低。
任務的執行時間:長、中和短。
任務的依賴性:是否依賴其他系統資源,如資料庫連接。
性質不同的任務可以用不同規模的線程池分開處理。CPU密集型任務應配置盡可能少的線程,如配置Ncpu+1個線程的線程池。由於IO密集型任務線程並不是一直在執行任務,則應配置多一點線程,如2*Ncpu。混合型的任務,如果可以拆分,將其拆分成一個CPU密集型任務和一個IO密集型任務,只要這兩個任務執行的時間相差不是太大,那麼分解後執行的吞吐量將高於串列執行的吞吐量。如果這兩個任務執行時間相差太大,則沒必要進行分解。可以通過Runtime.getRuntime().availableProcessors()方法獲得當前設備的CPU個數。

優先順序不同的任務可以使用優先順序隊列PriorityBlockingQueue來處理。它可以讓優先順序高的任務先執行。執行時間不同的任務可以交給不同規模的線程池來處理,或者可以使用優先順序隊列,讓執行時間短的任務先執行。依賴資料庫連接池的任務,因為線程提交SQL後需要等待資料庫返回結果,等待的時間越長,則CPU空閑時間就越長,那麼線程數應該設置得越大,這樣才能更好地利用CPU。

線程池中線程數量未達到coreSize時,這些線程處於什麼狀態?

這些線程處於RUNNING或者WAITING,RUNNING表示線程處於運行當中,WAITING表示線程阻塞等待在阻塞隊列上。當一個task submit給線程池時,如果當前線程池線程數量還未達到coreSize時,會創建線程執行task,否則將任務提交給阻塞隊列,然後觸發線程執行。(從submit內部調用的代碼也可以看出來)

ScheledThreadPoolExecutor

ScheledThreadPoolExecutor繼承自ThreadPoolExecutor。它主要用來在給定的延遲之後運行任務,或者定期執行任務。
ScheledThreadPoolExecutor的功能與Timer類似,但
ScheledThreadPoolExecutor功能更強大、更靈活。Timer對應的是單個後台線程,而
ScheledThreadPoolExecutor可以在構造函數中指定多個對應的後台線程數。

ScheledThreadPoolExecutor繼承自ThreadPoolExecutor,
ScheledThreadPoolExecutor和ThreadPoolExecutor的區別是,ThreadPoolExecutor獲取任務時是從BlockingQueue中獲取的,而
ScheledThreadPoolExecutor是從DelayedWorkQueue中獲取的(注意,DelayedWorkQueue是BlockingQueue的實現類)。

ScheledThreadPoolExecutor把待調度的任務(ScheledFutureTask)放到一個DelayQueue中,其中ScheledFutureTask主要包含3個成員變數:

sequenceNumber:任務被添加到ScheledThreadPoolExecutor中的序號;
time:任務將要被執行的具體時間;
period:任務執行的間隔周期。

ScheledThreadPoolExecutor會把待執行的任務放到工作隊列DelayQueue中,DelayQueue封裝了一個PriorityQueue,PriorityQueue會對隊列中的ScheledFutureTask進行排序,具體的排序比較演算法實現如下:

ScheledFutureTask在DelayQueue中被保存在一個PriorityQueue(基於數組實現的優先隊列,類似於堆排序中的優先隊列)中,在往數組中添加/移除元素時,會調用siftDown/siftUp來進行元素的重排序,保證元素的優先順序順序。

static class DelayedWorkQueue extends AbstractQueue<Runnable>
implements BlockingQueue<Runnable> {

private static final int INITIAL_CAPACITY = 16;
private RunnableScheledFuture<?>[] queue =
new RunnableScheledFuture<?>[INITIAL_CAPACITY];
private final ReentrantLock lock = new ReentrantLock();
private int size = 0;

private Thread leader = null;
private final Condition available = lock.newCondition();
}
從DelayQueue獲取任務的主要邏輯就在take()方法中,首先獲取lock,然後獲取queue[0],如果為null則await等待任務的來臨,如果非null查看任務是否到期,是的話就執行該任務,否則再次await等待。這里有一個leader變數,用來表示當前進行awaitNanos等待的線程,如果leader非null,表示已經有其他線程在進行awaitNanos等待,自己await等待,否則自己進行awaitNanos等待。

// DelayedWorkQueue
public RunnableScheledFuture<?> take() throws InterruptedException {
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
for (;;) {
RunnableScheledFuture<?> first = queue[0];
if (first == null)
available.await();
else {
long delay = first.getDelay(NANOSECONDS);
if (delay <= 0)
return finishPoll(first);
first = null; // don't retain ref while waiting
if (leader != null)
available.await();
else {
Thread thisThread = Thread.currentThread();
leader = thisThread;
try {
available.awaitNanos(delay);
} finally {
if (leader == thisThread)
leader = null;
}
}
}
}
} finally {
if (leader == null && queue[0] != null)
available.signal();
lock.unlock();
}
}
獲取到任務之後,就會執行task的run()方法了,即ScheledFutureTask.run():

public void run() {
boolean periodic = isPeriodic();
if (!canRunInCurrentRunState(periodic))
cancel(false);
else if (!periodic)
ScheledFutureTask.super.run();
else if (ScheledFutureTask.super.runAndReset()) {
setNextRunTime();
reExecutePeriodic(outerTask);
}
}
推薦閱讀:
JMM Java內存模型
happens-before那些事兒
為什麼說LockSupport是Java並發的基石?
責任鏈的2種實現方式,你更pick哪一種
2閱讀

㈥ java線程池中的線程數應該如何設置

java中線程池的監控可以檢測到正在執行的線程數。
通過線程池提供的參數進行監控。線程池裡有一些屬性在監控線程池的時候可以使用
taskCount:線程池需要執行的任務數量。
completedTaskCount:線程池在運行過程中已完成的任務數量。小於或等於taskCount。
largestPoolSize:線程池曾經創建過的最大線程數量。通過這個數據可以知道線程池是否滿過。如等於線程池的最大大小,則表示線程池曾經滿了。
getPoolSize:線程池的線程數量。如果線程池不銷毀的話,池裡的線程不會自動銷毀,所以這個大小隻增不+ getActiveCount:獲取活動的線程數。
通過擴展線程池進行監控。通過繼承線程池並重寫線程池的beforeExecute,afterExecute和terminated方法,我們可以在任務執行前,執行後和線程池關閉前干一些事情。如監控任務的平均執行時間,最大執行時間和最小執行時間等。這幾個方法在線程池裡是空方法。如:
protected void beforeExecute(Thread t, Runnable r) { }

㈦ java線程池怎麼實現

要想理解清楚java線程池實現原理,明白下面幾個問題就可以了:

(1):線程池存在哪些狀態,這些狀態之間是如何進行切換的呢?

(2):線程池的種類有哪些?

(3):創建線程池需要哪些參數,這些參數的具體含義是什麼?

(4):將任務添加到線程池之後運行流程?

(5):線程池是怎麼做到重用線程的呢?

(6):線程池的關閉

首先回答第一個問題:線程池存在哪些狀態;

查看ThreadPoolExecutor源碼便知曉:

[java]view plain

  • //runStateisstoredinthehigh-orderbits

  • privatestaticfinalintRUNNING=-1<<COUNT_BITS;

  • privatestaticfinalintSHUTDOWN=0<<COUNT_BITS;

  • privatestaticfinalintSTOP=1<<COUNT_BITS;

  • privatestaticfinalintTIDYING=2<<COUNT_BITS;

  • =3<<COUNT_BITS;

  • 存在5種狀態:

    <1>Running:可以接受新任務,同時也可以處理阻塞隊列裡面的任務;

    <2>Shutdown:不可以接受新任務,但是可以處理阻塞隊列裡面的任務;

    <3>Stop:不可以接受新任務,也不處理阻塞隊列裡面的任務,同時還中斷正在處理的任務;

    <4>Tidying:屬於過渡階段,在這個階段表示所有的任務已經執行結束了,當前線程池中是不存在有效的線程的,並且將要調用terminated方法;

    <5>Terminated:終止狀態,這個狀態是在調用完terminated方法之後所處的狀態;

    那麼這5種狀態之間是如何進行轉換的呢?查看ThreadPoolExecutor源碼裡面的注釋便可以知道啦:

    [java]view plain

  • *RUNNING->SHUTDOWN

  • *Oninvocationofshutdown(),perhapsimplicitlyinfinalize()

  • *(RUNNINGorSHUTDOWN)->STOP

  • *OninvocationofshutdownNow()

  • *SHUTDOWN->TIDYING

  • *Whenbothqueueandpoolareempty

  • *STOP->TIDYING

  • *Whenpoolisempty

  • *TIDYING->TERMINATED

  • *Whentheterminated()hookmethodhascompleted

  • 從上面可以看到,在調用shutdown方法的時候,線程池狀態會從Running轉換成Shutdown;在調用shutdownNow方法的時候,線程池狀態會從Running/Shutdown轉換成Stop;在阻塞隊列為空同時線程池為空的情況下,線程池狀態會從Shutdown轉換成Tidying;在線程池為空的情況下,線程池狀態會從Stop轉換成Tidying;當調用terminated方法之後,線程池狀態會從Tidying轉換成Terminate;

    在明白了線程池的各個狀態以及狀態之間是怎麼進行切換之後,我們來看看第二個問題,線程池的種類:

    (1):CachedThreadPool:緩存線程池,該類線程池中線程的數量是不確定的,理論上可以達到Integer.MAX_VALUE個,這種線程池中的線程都是非核心線程,既然是非核心線程,那麼就存在超時淘汰機制了,當裡面的某個線程空閑時間超過了設定的超時時間的話,就會回收掉該線程;

    (2):FixedThreadPool:固定線程池,這類線程池中是只存在核心線程的,對於核心線程來說,如果我們不設置allowCoreThreadTimeOut屬性的話是不存在超時淘汰機制的,這類線程池中的corePoolSize的大小是等於maximumPoolSize大小的,也就是說,如果線程池中的線程都處於活動狀態的話,如果有新任務到來,他是不會開辟新的工作線程來處理這些任務的,只能將這些任務放到阻塞隊列裡面進行等到,直到有核心線程空閑為止;

    (3):ScheledThreadPool:任務線程池,這種線程池中核心線程的數量是固定的,而對於非核心線程的數量是不限制的,同時對於非核心線程是存在超時淘汰機制的,主要適用於執行定時任務或者周期性任務的場景;

    (4):SingleThreadPool:單一線程池,線程池裡面只有一個線程,同時也不存在非核心線程,感覺像是FixedThreadPool的特殊版本,他主要用於確保任務在同一線程中的順序執行,有點類似於進行同步吧;

    接下來我們來看第三個問題,創建線程池需要哪些參數:

    同樣查看ThreadPoolExecutor源碼,查看創建線程池的構造函數:

    [java]view plain

  • publicThreadPoolExecutor(intcorePoolSize,

  • intmaximumPoolSize,

  • longkeepAliveTime,

  • TimeUnitunit,

  • BlockingQueue<Runnable>workQueue,

  • ThreadFactorythreadFactory,

  • )

  • 不管你調用的是ThreadPoolExecutor的哪個構造函數,最終都會執行到這個構造函數的,這個構造函數有7個參數,正是由於對這7個參數值的賦值不同,造成生成不同類型的線程池,比如我們常見的CachedThreadPoolExecutor、FixedThreadPoolExecutor

    SingleThreadPoolExecutor、ScheledThreadPoolExecutor,我們老看看這幾個參數的具體含義:

    <1>corePoolSize:線程池中核心線程的數量;當提交一個任務到線程池的時候,線程池會創建一個線程來執行執行任務,即使有其他空閑的線程存在,直到線程數達到corePoolSize時不再創建,這時候會把提交的新任務放入到阻塞隊列中,如果調用了線程池的preStartAllCoreThreads方法,則會在創建線程池的時候初始化出來核心線程;

    <2>maximumPoolSize:線程池允許創建的最大線程數;如果阻塞隊列已經滿了,同時已經創建的線程數小於最大線程數的話,那麼會創建新的線程來處理阻塞隊列中的任務;

    <3>keepAliveTime:線程活動保持時間,指的是工作線程空閑之後繼續存活的時間,默認情況下,這個參數只有線程數大於corePoolSize的時候才會起作用,即當線程池中的線程數目大於corePoolSize的時候,如果某一個線程的空閑時間達到keepAliveTime,那麼這個線程是會被終止的,直到線程池中的線程數目不大於corePoolSize;如果調用allowCoreThreadTimeOut的話,在線程池中線程數量不大於corePoolSize的時候,keepAliveTime參數也可以起作用的,知道線程數目為0為止;

    <4>unit:參數keepAliveTime的時間單位;

    <5>workQueue:阻塞隊列;用於存儲等待執行的任務,有四種阻塞隊列類型,ArrayBlockingQueue(基於數組的有界阻塞隊列)、LinkedBlockingQueue(基於鏈表結構的阻塞隊列)、SynchronousQueue(不存儲元素的阻塞隊列)、PriorityBlockingQueue(具有優先順序的阻塞隊列);

    <6>threadFactory:用於創建線程的線程工廠;

    <7>handler:當阻塞隊列滿了,且沒有空閑線程的情況下,也就是說這個時候,線程池中的線程數目已經達到了最大線程數量,處於飽和狀態,那麼必須採取一種策略來處理新提交的任務,我們可以自己定義處理策略,也可以使用系統已經提供給我們的策略,先來看看系統為我們提供的4種策略,AbortPolicy(直接拋出異常)、CallerRunsPolicy(只有調用者所在的線程來運行任務)、DiscardOldestPolicy(丟棄阻塞隊列中最近的一個任務,並執行當前任務)、Discard(直接丟棄);

    接下來就是將任務添加到線程池之後的運行流程了;

    我們可以調用submit或者execute方法,兩者最大的區別在於,調用submit方法的話,我們可以傳入一個實現Callable介面的對象,進而能在當前任務執行結束之後通過Future對象獲得任務的返回值,submit內部實際上還是執行的execute方法;而調用execute方法的話,是不能獲得任務執行結束之後的返回值的;此外,調用submit方法的話是可以拋出異常的,但是調用execute方法的話,異常在其內部得到了消化,也就是說異常在其內部得到了處理,不會向外傳遞的;

    因為submit方法最終也是會執行execute方法的,因此我們只需要了解execute方法就可以了:

    在execute方法內部會分三種情況來進行處理:

    <1>:首先判斷當前線程池中的線程數量是否小於corePoolSize,如果小於的話,則直接通過addWorker方法創建一個新的Worker對象來執行我們當前的任務;

    <2>:如果說當前線程池中的線程數量大於corePoolSize的話,那麼會嘗試將當前任務添加到阻塞隊列中,然後第二次檢查線程池的狀態,如果線程池不在Running狀態的話,會將剛剛添加到阻塞隊列中的任務移出,同時拒絕當前任務請求;如果第二次檢查發現當前線程池處於Running狀態的話,那麼會查看當前線程池中的工作線程數量是否為0,如果為0的話,就會通過addWorker方法創建一個Worker對象出來處理阻塞隊列中的任務;

    <3>:如果原先線程池就不處於Running狀態或者我們剛剛將當前任務添加到阻塞隊列的時候出現錯誤的話,那麼會去嘗試通過addWorker創建新的Worker來處理當前任務,如果添加失敗的話,則拒絕當前任務請求;

    可以看到在上面的execute方法中,我們僅僅只是檢查了當前線程池中的線程數量有沒有超過corePoolSize的情況,那麼當前線程池中的線程數量有沒有超過maximumPoolSize是在哪裡檢測的呢?實際上是在addWorker方法裡面了,我們可以看下addWorker裡面的一段代碼:

    [java]view plain

  • if(wc>=CAPACITY||

  • wc>=(core?corePoolSize:maximumPoolSize))

  • returnfalse;

  • 如果當前線程數量超過maximumPoolSize的話,直接就會調用return方法,返回false;

    其實到這里我們很明顯可以知道,一個線程池中線程的數量實際上就是這個線程池中Worker的數量,如果Worker的大小超過了corePoolSize,那麼任務都在阻塞隊列裡面了,Worker是Java對我們任務的一個封裝類,他的聲明是醬紫的:

    [java]view plain

  • privatefinalclassWorker

  • implementsRunnable

  • 可以看到他實現了Runnable介面,他是在addWorker方法裡面通過new Worker(firstTask)創建的,我們來看看他的構造函數就知道了:

    [java]view plain

  • Worker(RunnablefirstTask){

  • setState(-1);//

  • this.firstTask=firstTask;

  • this.thread=getThreadFactory().newThread(this);

  • }

  • 而這里的firstTask其實就是我們調用execute或者submit的時候傳入的那個參數罷了,一般來說這些參數是實現Callable或者Runnable介面的;

    在通過addWorker方法創建出來Worker對象之後,這個方法的最後會執行Worker內部thread屬性的start方法,而這個thread屬性實際上就是封裝了Worker的Thread,執行他的start方法實際上執行的是Worker的run方法,因為Worker是實現了Runnable介面的,在run方法裡面就會執行runWorker方法,而runWorker方法裡面首先會判斷當前我們傳入的任務是否為空,不為空的話直接就會執行我們通過execute或者submit方法提交的任務啦,注意一點就是我們雖然會通過submit方法提交實現了Callable介面的對象,但是在調用submit方法的時候,其實是會將Callable對象封裝成實現了Runnable介面對象的,不信我們看看submit方法源碼是怎麼實現的:

    [java]view plain

  • public<T>Future<T>submit(Callable<T>task){

  • if(task==null)thrownewNullPointerException();

  • RunnableFuture<T>ftask=newTaskFor(task);

  • execute(ftask);

  • returnftask;

  • }

  • 看到沒有呢,實際上在你傳入實現了Callable介面對象的時候,在submit方法裡面是會將其封裝成RunnableFuture對象的,而RunnableFuture介面是繼承了Runnable介面的;那麼說白了其實就是直接執行我們提交任務的run方法了;如果為空的話,則會通過getTask方法從阻塞隊列裡面拿出一個任務去執行;在任務執行結束之後繼續從阻塞隊列裡面拿任務,直到getTask的返回值為空則退出runWorker內部循環,那麼什麼情況下getTask返回為空呢?查看getTask方法的源碼注釋可以知道:在Worker必須需要退出的情況下getTask會返回空,具體什麼情況下Worker會退出呢?(1):當Worker的數量超過maximumPoolSize的時候;(2):當線程池狀態為Stop的時候;(3):當線程池狀態為Shutdown並且阻塞隊列為空的時候;(4):使用等待超時時間從阻塞隊列中拿數據,但是超時之後仍然沒有拿到數據;

    如果runWorker方法退出了它裡面的循環,那麼就說明當前阻塞隊列裡面是沒有任務可以執行的了,你可以看到在runWorker方法內部的finally語句塊中執行了processWorkerExit方法,用來對Worker對象進行回收操作,這個方法會傳入一個參數表示需要刪除的Worker對象;在進行Worker回收的時候會調用tryTerminate方法來嘗試關閉線程池,在tryTerminate方法裡面會檢查是否有Worker在工作,檢查線程池的狀態,沒問題的話就會將當前線程池的狀態過渡到Tidying,之後調用terminated方法,將線程池狀態更新到Terminated;

    從上面的分析中,我們可以看出線程池運行的4個階段:

    (1):poolSize < corePoolSize,則直接創建新的線程(核心線程)來執行當前提交的任務;

    (2):poolSize = corePoolSize,並且此時阻塞隊列沒有滿,那麼會將當前任務添加到阻塞隊列中,如果此時存在工作線程(非核心線程)的話,那麼會由工作線程來處理該阻塞隊列中的任務,如果此時工作線程數量為0的話,那麼會創建一個工作線程(非核心線程)出來;

    (3):poolSize = corePoolSize,並且此時阻塞隊列已經滿了,那麼會直接創建新的工作線程(非核心線程)來處理阻塞隊列中的任務;

    (4):poolSize = maximumPoolSize,並且此時阻塞隊列也滿了的話,那麼會觸發拒絕機制,具體決絕策略採用的是什麼就要看我們創建ThreadPoolExecutor的時候傳入的RejectExecutionHandler參數了;

    接下來就是線程池是怎麼做到重用線程的呢?

    個人認為線程池裡面重用線程的工作是在getTask裡面實現的,在getTask裡面是存在兩個for死循環嵌套的,他會不斷的從阻塞對列裡面取出需要執行的任務,返回給我們的runWorker方法裡面,而在runWorker方法裡面只要getTask返回的任務不是空就會執行該任務的run方法來處理它,這樣一直執行下去,直到getTask返回空為止,此時的情況就是阻塞隊列裡面沒有任務了,這樣一個線程處理完一個任務之後接著再處理阻塞隊列中的另一個任務,當然在線程池中的不同線程是可以並發處理阻塞隊列中的任務的,最後在阻塞隊列內部不存在任務的時候會去判斷是否需要回收Worker對象,其實Worker對象的個數就是線程池中線程的個數,至於什麼情況才需要回收,上面已經說了,就是四種情況了;

    最後就是線程池是怎樣被關閉的呢?

    涉及到線程池的關閉,需要用到兩個方法,shutdown和shutdownNow,他們都是位於ThreadPoolExecutor裡面的,對於shutdown的話,他會將線程池狀態切換成Shutdown,此時是不會影響對阻塞隊列中任務執行的,但是會拒絕執行新加進來的任務,同時會回收閑置的Worker;而shutdownNow方法會將線程池狀態切換成Stop,此時既不會再去處理阻塞隊列裡面的任務,也不會去處理新加進來的任務,同時會回收所有Worker;

㈧ Java線程池幾個參數

corePollSize:核心線程數。
maximumPoolSize:最大線程數。
keepAliveTime:空閑的線程保留的時間。
TimeUnit:空閑線程的保留時間單位。
BlockingQueue<Runnable>:阻塞隊列,存儲等待執行的任務。
ThreadFactory:線程工廠,用來創建線程。
RejectedExecutionHandler:隊列已滿,而且任務量大於最大線程的異常處理策略。

㈨ 線程池有哪些配置參數,各自的作用是什麼

線程池是為突然大量爆發的線程設計的,通過有限的幾個固定線程為大量的操作服務,減少了創建和銷毀線程所需的時間,從而提高效率。如果一個線程的時間非常長,就沒必要用線程池了(不是不能作長時間操作,而是不宜。),況且還不能控制線程池中線程的開始、掛起、和中止。

㈩ 【Java基礎】線程池的原理是什麼

什麼是線程池?

總歸為:池化技術 ---》資料庫連接池 緩存架構 緩存池 線程池 內存池,連接池,這種思想演變成緩存架構技術---> JDK設計思想有千絲萬縷的聯系

首先我們從最核心的ThreadPoolExecutor類中的方法講起,然後再講述它的實現原理,接著給出了它的使用示例,最後討論了一下如何合理配置線程池的大小。

Java 中的 ThreadPoolExecutor 類

java.uitl.concurrent.ThreadPoolExecutor 類是線程池中最核心的一個類,因此如果要透徹地了解Java 中的線程池,必須先了解這個類。下面我們來看一下 ThreadPoolExecutor 類的具體實現源碼。

在 ThreadPoolExecutor 類中提供了四個構造方法:

熱點內容
台灣免費伺服器地址 發布:2024-11-14 13:46:37 瀏覽:900
安卓哪裡可以聽空間音頻 發布:2024-11-14 13:39:39 瀏覽:481
什麼軟體查看手機配置 發布:2024-11-14 13:35:25 瀏覽:726
androidsql查詢語句 發布:2024-11-14 13:35:09 瀏覽:893
linux查看線程數 發布:2024-11-14 13:34:21 瀏覽:771
如何在實驗室搭建伺服器 發布:2024-11-14 13:33:36 瀏覽:632
c語言多個條件 發布:2024-11-14 13:32:36 瀏覽:71
緩存行原理 發布:2024-11-14 13:08:56 瀏覽:432
簡單的vb編程 發布:2024-11-14 13:06:45 瀏覽:523
綠色linux 發布:2024-11-14 12:56:11 瀏覽:350