P6033 合併果子 加強版
執行緒池:
避免了建立執行緒和銷燬執行緒的資源損耗。
Executors提供四種執行緒池:
- newCachedThreadPool :快取執行緒池,如果執行緒池長度超過處理需要,可回收空閒執行緒,若無可回收,則新建執行緒。
- newFixedThreadPool : 定長執行緒池,可控制執行緒最大併發數,超出的執行緒會在佇列中等待。
- newScheduledThreadPool : 計劃執行緒池,支援定時及週期性任務執行。
- newSingleThreadExecutor :單執行緒執行緒池,用唯一的執行緒來執行任務,保證所有任務按照指定順序(FIFO, LIFO, 優先順序)執行。
先了解下執行緒池必備的以下幾個屬性:
poolSize : 當前程式執行的執行緒數。
coreSize: 核心執行緒數。
MaximumPoolSize: 執行緒池中最大執行緒數。
keepAliveTime:執行緒空閒時間超過,則會超時退出。
BlockingQueue :設定一個阻塞用來存放將要執行的等待任務。
(執行緒安全:保證取佇列元素時一個只能取一個,且頭元素沒有時不能取,滿時不能加入)
執行緒池的執行流程:
當需要執行一個任務時:
poolsize < coresize ->開啟一個新的執行緒執行
poolsize = coresize && 佇列未滿 -> 這時已經達到了核心執行緒數,執行緒不夠了,加入到阻塞佇列中,慢慢執行。
Poolsize < MaximumPoolSize && 佇列已滿 -> 這時佇列都滿了,那不行啊 ,快爆了,在開啟一個新執行緒吧。
(通常超出核心執行緒的執行緒是“借”的,也就是說超出核心執行緒的情況算是一種能夠預見的異常情況,並且這種情況並不常常發生(如果常常發生,那我想你應該調整你的核心執行緒數了)
Poolsize = MaximumPoolSize && 佇列滿了 -> 已經所有辦法用盡了,會根據飽和策略RejectedExecutionHandler拒絕新的任務。
(如果設定了keepAliveTime,可以通過以下配置來確定小於corePoolSize時,是否啟動超時處理。
但是對於超出MaximumPoolSize,一定會進行超時處理,因為這些執行緒本就不是正常情況。
allowCoreThreadTimeOut:是否允許核心執行緒超時退出。
如果該值為false(預設),且poolSize<=corePoolSize,執行緒池都會保證這些核心執行緒處於存活狀態,不會超時退出。(所以只要不是超過corePoolSize,執行緒都會空閒而不是銷燬)
如果為true,則不論poolSize的大小,都允許超時退出。
如果poolSize>corePoolSize,則該引數不論true還是false,都允許超時退出。
)
(所以大概流程:執行一個任務,如果執行緒少於coresize則啟動新執行緒,如果等於了coresize,則加入佇列等待。如果佇列滿了,實再不行了,則在開啟新執行緒。如果執行緒等於最大執行緒了,執行飽和策略拒絕任務)
自定義執行緒:可以發現,其實就是我們說的那幾個引數,特別簡單。
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue)
/**
*coresieze和maximumPool相同,所以這也是為什麼他是固定大小。
並且阻塞LinkedBlockingQueue沒有初始化大小,是個無界佇列。所以進來的任務,執行緒不夠就會都加入佇列等待。
keepAliveTime:0,預設超過最大執行緒才會啟用超時,意味著:一超過最大執行緒數,立即關閉。
*/
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
/**
* coresieze和maximumPool都為1
*/
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
/**
* 核心執行緒為0,最大執行緒數無限大。意味著,所有執行緒都有keepalivetime,都有一小段的生命週期。(快取執行緒也就是說,執行的執行緒會快取一段時間,然後到時間釋放)在使用這類無限大的執行緒池時,非常容易記憶體消耗殆盡。這也是為什麼阿里不建議使用Executors建立執行緒池。其實自定義執行緒池非常好用,而且自己看了就會非常明確。
SynchronousQueue(同步佇列)一個不儲存元素的阻塞佇列,每個插入操作必須等到另一個執行緒呼叫移除操作(即每個任務都必須有執行緒接管,否則不加入),否則插入操作一直處於阻塞狀態
*/
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
/**
*定時執行緒使用的是ThreadPoolExecutor子類
*/
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
public static ScheduledExecutorService newSingleThreadScheduledExecutor() {
return new DelegatedScheduledExecutorService
(new ScheduledThreadPoolExecutor(1));
}
使用executorService.execute(Runnable)或executorService.submit(Callable)
(執行緒是執行緒Thread,任務是任務Runnable。
建立一個Runnable,只是實現了run方法,這是任務,不是執行緒。他並不能執行,所以需要傳入Thread中,然後執行。
啟動執行緒的唯一方法就是通過Thread類的start()例項方法。
)