1. 程式人生 > >自定義執行緒池ThreadPoolExecutor

自定義執行緒池ThreadPoolExecutor

public ThreadPoolExecutor (int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler)

Added in API level 1

Creates a new ThreadPoolExecutor with the given initial parameters.

Parameters

corePoolSize

the number of threads to keep in the pool, even if they are idle, unless allowCoreThreadTimeOut is set

maximumPoolSize

the maximum number of threads to allow in the pool

keepAliveTime

when the number of threads is greater than the core, this is the maximum time that excess idle threads will wait for new tasks before terminating.

unit

the time unit for the keepAliveTime argument

workQueue

the queue to use for holding tasks before they are executed. This queue will hold only the Runnable tasks submitted by the execute method.

threadFactory

the factory to use when the executor creates a new thread

handler

the handler to use when execution is blocked because the thread bounds and queue capacities are reached

Throws

IllegalArgumentException

if one of the following holds: corePoolSize < 0 keepAliveTime < 0 maximumPoolSize <= 0 maximumPoolSize < corePoolSize

NullPointerException

if workQueue or threadFactory or handler is null

例項:

BlockingQueue<Runnable> sPoolWorkQueue = new LinkedBlockingQueue<Runnable>(3);//fixedpool or singlepool

Executor executor = new ThreadPoolExecutor(2, 4, 0, TimeUnit.SECONDS, sPoolWorkQueue);
解釋一下引數:
corePoolSize=1:
表示執行緒池executor執行的執行緒數小於等於佇列容量(LinkedBlockingQueue的構造引數:2)加corePoolSize之和時,只會建立corePoolSize個執行緒。
例如,本例中,
executor.execute(r1);//r1表示Runnable物件。
executor.execute(r2);
executor.execute(r3);
executor.execute(r4);
executor.execute(r5);
那麼,執行邏輯就是,先執行r1,r2,同時r3,r4,r5新增到佇列中,r1,r2執行完後,複用r1,r2的執行緒來執行r3,r4,同理r3,r4完後複用其中一個執行緒來執行r5.
1、這個過程只開闢了2個執行緒.(因為corePoolSize為2,也就是執行緒數<=corePoolSize時,直接new Thread())
2、executor執行的執行緒數為5,首先執行r1,r2,然後還剩r3,r4,r5,而佇列容量正好為3,所以能夠容納r3,r4,r5.所以就依據corePoolSize的規定,一次執行兩個執行緒。
那麼如果佇列容納不了剩下的執行緒怎麼辦?就要看下面這個引數:
maximumPoolSize=4:
表示當executor要執行的執行緒數超過corePoolSize+佇列容量之和時,可打破corePoolSize規定,使用maximunPoolSize規定來建立新執行緒個數。
例如,本例中,
executor.execute(r1);
executor.execute(r2);
executor.execute(r3);
executor.execute(r4);
executor.execute(r5);
executor.execute(r6);
executor.execute(r7);
如果按照corePoolSize的規定,一次執行兩個執行緒,那麼剩下的5個執行緒,佇列容納不了。所以(在預設RejectedExecutionHandler下)會拋異常。
此時,就會啟動一級應急:將corePoolSize限制擴充到maximumPoolSize,即,執行緒數<=maximumPoolSize,直接new Thread().
那麼,邏輯就會:先執行r1,r2,r3,r4,同時將r5,r6,r7新增到佇列中,等r1,r2,r3,r4執行完後,複用其中三個執行緒來執行r5,r6,r7.
那麼,如果executor執行8個執行緒,此時佇列又容納不了剩下的執行緒了。怎麼辦呢?那就看下面引數:
handler=new AbortPolicy():
AbortPolicy,實現RejectedExecutionHandler介面,實現策略是拋異常。這也是預設handler的值。
CallerRunsPolicy,實現反覆地執行“try again”.(好單純...)。注意:它會啟用主執行緒來幫助,容易拋NetworkOnMainThreadException
DiscardPolicy,實現反覆地執行“默默地拋棄容納不了的執行緒任務”.(好委屈...)
DiscardOldestPolicy,實現反覆地執行“拋棄佇列中最先進入佇列的執行緒任務,然後自己插入佇列”.(好強硬...)
需要注意,
使用CallerRunsPolicy,會將所有執行緒都執行到,也不可避免的會使用主執行緒來載入一個執行緒任務。一次同時建立maximumPoolSize個執行緒,加上主執行緒,就是一次執行maximumPoolSize+1個執行緒任務,等執行完後才會,再執行maximunPoolSize+1個執行緒任務,當然建立的maximumPoolSize個執行緒也會複用。
顯然這種策略保證了每一個執行緒任務都會執行,但犧牲了效能。
而,DiscardPolicy和DiscardOldestPolicy都會拋棄容納不了的執行緒任務,保證了效能。
總的來說,
1、由於通常使用佇列LinkedBlockingQueue的預設容量為Integer.MAX_VALUE,所以通常maximumPoolSize值用不到,但是不能小於corePoolSize。
也就是說,如果用LinkedBlockingQueue佇列,且容量使用預設值,那麼maximumPoolSize完全可以設定為maximumPoolSize=corePoolSize.
所以Executors中的newFixedThreadPool和newSingleThreadExecutor的corePoolSize和maximumPoolSize值是一樣的。所以將newFixedThreadPool和newSingleThreadExecutor中corePoolSize和maximumPoolSize的值一樣的原因歸結於,雙重限定執行緒池大小的解釋是錯誤的。
2、Executors中的不止newCachedThreadPool()執行緒池會複用已建立的執行緒,newFixedThreadPool(int),newSingleThreadPool和newScheduledThreadPool()都會複用已建立的執行緒。
區別在於:
newFixedThreadPool(int),newSingleThreadPool,併發執行緒數受限。快取不失效。
newCachedThreadPool(),併發執行緒數不限制。一分鐘後快取失效。
原因在於:
newFixedThreadPool()和newSingleThreadPool,使用的佇列為LinkedBlockingQueue,該佇列會長期持有固定數量的執行緒,不釋放,所以使用LinkedBlockingQueue佇列,設定keepAliveTime是無效的。
newCachedThreadPool,使用SynchronousQueue佇列,該佇列支援keepAliveTime,但設定corePoolSize無效。需要多少執行緒就會建立多少執行緒。
進一步對比:
SynchronousQueue佇列沒有任何佇列容量。所以maximumPoolSize要儘量大,corePoolSize無用,可以設為0。(這就是newCachedThreadPool將corePoolSize設為0的原因)所以只要執行緒數超過maximumPoolSize,就是拋異常java.lang.StackOverflowError.這樣就解釋了,為什麼SynchronousQueue佇列設定corePoolSize無用。
LinkedBlockingQueue佇列預設是不支援keepAliveTime的,只有設定了allowCoreThreadTimeOut(true)後,才支援keepAliveTime.所以預設情況下,設定keepAliveTime是無效的。
所以看來,newCachedThreadPool更好用些,能夠保證所有網路介面併發請求,且一分鐘後,釋放這些執行緒資源。
但是對於部分APP,大量“多併發網路介面”的介面,還是得用newFixedThreadPool()來控制一下。多併發必然多建立執行緒,會影響效能。
多少併發才算多併發呢,我覺得是5個以上的併發。
比如,一個APP其中有十幾個介面,這些介面每個介面都有超過5個網路介面。那麼可以考慮用newFixedThreadPool()來控制一下效能。