Java線程 一般采用new Thread().start();的方式開啟一個新的線程池實例。但是這樣的會無節(jié)制的創(chuàng)建線程俱箱、回收線程奏窑,造成頻繁的GC。
線程池的由來就是為了解決此問題的舵鳞。線程池旨在線程的復(fù)用,這就可以節(jié)約我們用以往的方式創(chuàng)建線程和銷毀所消耗的時間琢蛤,減少線程頻繁調(diào)度的開銷蜓堕,從而節(jié)約系統(tǒng)資源,提高系統(tǒng)吞吐量博其。
在Java線程池概念中套才,ExecutorService,它是一個接口慕淡,其實如果要從真正意義上來說背伴,它可以叫做線程池的服務(wù),因為它提供了眾多接口api來控制線程池中的線程,而真正意義上的線程池就是:ThreadPoolExecutor傻寂,它實現(xiàn)了ExecutorService接口息尺,并封裝了一系列的api使得它具有線程池的特性,其中包括工作隊列疾掰、核心線程數(shù)搂誉、最大線程數(shù)等。
ThreadPoolExecutor
ThreadPoolExecutor參數(shù)
- int corePoolSize ——線程池中核心線程的數(shù)量静檬。
- int maximumPoolSize ——線程池中最大線程數(shù)量炭懊。
- long keepAliveTime——非核心線程的超時時長,當(dāng)系統(tǒng)中非核心線程閑置時間超過keepAliveTime之后拂檩,則會被回收侮腹。如果ThreadPoolExecutor的allowCoreThreadTimeOut屬性設(shè)置為true,則該參數(shù)也表示核心線程的超時時長广恢。
- TimeUnit unit ——時間單位凯旋,有納秒、微秒钉迷、毫秒、秒钠署、分糠聪、時、天等谐鼎。
- BlockingQueue< Runnable > workQueue ——線程池中的任務(wù)隊列舰蟆,該隊列主要用來存儲已經(jīng)被提交但是尚未執(zhí)行的任務(wù)。
- ThreadFactory threadFactory —— 線程工廠狸棍,為了給線程池提供創(chuàng)建線程的功能身害。
- RejectedExecutionHandler handler——拒絕策略,當(dāng)線程無法執(zhí)行新任務(wù)時(一般是由于線程池中的線程數(shù)量已經(jīng)達到最大數(shù)或者線程池關(guān)閉導(dǎo)致的)草戈,默認情況下塌鸯,當(dāng)線程池?zé)o法處理新線程時,會拋出一個RejectedExecutionException唐片。
任務(wù)隊列
用于保存等待執(zhí)行的任務(wù)的阻塞隊列丙猬,可以選擇以下幾個:
- ArrayBlockingQueue:基于數(shù)組的阻塞隊列,按照FIFO原則進行排序费韭。
- LinkedBlockingQueue:基于鏈表的阻塞隊列茧球,按照FIFO原則對元素進行排序。
- SynchronousQueue:一個不儲存元素的阻塞隊列星持,每一個插入操作必須等到另外一個線程調(diào)用移除操作抢埋,否則插入操作一直處于阻塞狀態(tài)。
- PriorityBlockingQueue:一個具有優(yōu)先級的無限阻塞隊列。
任務(wù)拒絕策略
- ThreadPoolExecutor.AbortPolicy:丟棄任務(wù)并拋出RejectedExecutionException異常揪垄。
- ThreadPoolExecutor.DiscardPolicy:也是丟棄任務(wù)穷吮,但是不拋出異常。
- ThreadPoolExecutor.DiscardOldestPolicy:丟棄隊列最前面的任務(wù)福侈,然后重新嘗試執(zhí)行任務(wù)(重復(fù)此過程)
- ThreadPoolExecutor.CallerRunsPolicy:由調(diào)用線程處理該任務(wù)
ThreadPoolExecutor工作規(guī)則
- 如果線程池中的線程數(shù)未達到核心線程數(shù)酒来,則會立馬啟用一個核心線程去執(zhí)行。
- 如果線程池中的線程數(shù)已經(jīng)達到核心線程數(shù)肪凛,且workQueue未滿堰汉,則將新線程放入workQueue中等待執(zhí)行。
- 如果線程池中的線程數(shù)已經(jīng)達到核心線程數(shù)且workQueue已滿伟墙,但未超過非核心線程數(shù)翘鸭,則開啟一個非核心線程來執(zhí)行任務(wù)。
- 如果線程池中的線程數(shù)已經(jīng)超過非核心線程數(shù)戳葵,則拒絕執(zhí)行該任務(wù)就乓。
注:核心線程、非核心線程和線程隊列拱烁,是三個概念哦生蚁,別混為一談。
ThreadPoolExecutor封裝案例
/**
* 類描述:線程池管理器
*
* @author jinzifu
* @Email jinzifu123@163.com
* @date 2017/12/7 1150
*/
public class ThreadPoolManager {
/**
* 核心線程的數(shù)量戏自。當(dāng)前設(shè)備可用處理器核心數(shù)*2 + 1,能夠讓cpu的效率得到最大程度執(zhí)行邦投。
*/
private int corePoolSize;
/**
* 最大線程數(shù)量,表示當(dāng)緩沖隊列滿的時候能繼續(xù)容納的等待任務(wù)的數(shù)量擅笔。
*/
private int maximumPoolSize;
/**
* 非核心線程的超時時長志衣,當(dāng)系統(tǒng)中非核心線程閑置時間超過keepAliveTime之后,則會被回收猛们。
* 如果ThreadPoolExecutor的allowCoreThreadTimeOut屬性設(shè)置為true念脯,則該參數(shù)也表示核心線程的超時時長。
*/
private long keepAliveTime = 1;
/**
* 時間單位弯淘,有納秒绿店、微秒、毫秒耳胎、秒惯吕、分、時怕午、天等废登。
*/
private TimeUnit timeUnit = TimeUnit.HOURS;
private ThreadPoolExecutor threadPoolExecutor;
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
private ThreadPoolManager() {
init();
}
private static class SingleClass {
private static final ThreadPoolManager THREAD_POOL_MANAGER = new ThreadPoolManager();
}
/**
* 靜態(tài)內(nèi)部類單例模式
*
* @return
*/
public static ThreadPoolManager getInstance() {
return SingleClass.THREAD_POOL_MANAGER;
}
/**
* 配置線程池屬性
* 部分參考AsyncTask的配置設(shè)計
*/
private void init() {
corePoolSize = CPU_COUNT + 1;
maximumPoolSize = CPU_COUNT * 2 + 1;
threadPoolExecutor = new ThreadPoolExecutor(
corePoolSize,
maximumPoolSize,
keepAliveTime,
timeUnit,
new LinkedBlockingDeque<Runnable>(),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy());
}
/**
* 執(zhí)行任務(wù)
*
* @param runnable
*/
public void execute(Runnable runnable) {
if (runnable != null) {
threadPoolExecutor.execute(runnable);
}
}
/**
* 從線程池移除任務(wù)
*
* @param runnable
*/
public void remove(Runnable runnable) {
if (runnable != null) {
threadPoolExecutor.remove(runnable);
}
}
}
FixedThreadPool
/**
* Creates a thread pool that reuses a fixed number of threads
* operating off a shared unbounded queue. At any point, at most
* {@code nThreads} threads will be active processing tasks.
* If additional tasks are submitted when all threads are active,
* they will wait in the queue until a thread is available.
* If any thread terminates due to a failure during execution
* prior to shutdown, a new one will take its place if needed to
* execute subsequent tasks. The threads in the pool will exist
* until it is explicitly {@link ExecutorService#shutdown shutdown}.
*
* @param nThreads the number of threads in the pool
* @return the newly created thread pool
* @throws IllegalArgumentException if {@code nThreads <= 0}
*/
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
從方法注釋和參數(shù)配置來看,核心線程數(shù)和最大線程數(shù)相同(意味著沒有非核心線程郁惜,且線程池處于北ぞ啵活狀態(tài)以等待任務(wù)甲锡,除非調(diào)用shutdown shutdown關(guān)閉線程隊列中的任務(wù)),超過核心線程數(shù)的任務(wù)都要進入線程隊列中等待羽戒,當(dāng)核心線程處于閑置狀態(tài)時就繼續(xù)執(zhí)行隊列里的任務(wù)缤沦。線程池隊列是沒有參數(shù),說明隊列長度是默認的Integer.MAX_VALUE(2的31次方減1)易稠。
特點:固定設(shè)置線程數(shù)缸废,快速響應(yīng)。
外部調(diào)用示例:
ExecutorService executorService = Executors.newFixedThreadPool(3);
executorService.execute(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 20; i++) {
try {
Thread.sleep(1000);
Log.d(TAG, "" + i);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
SingleThreadExecutor
/**
* Creates an Executor that uses a single worker thread operating
* off an unbounded queue. (Note however that if this single
* thread terminates due to a failure during execution prior to
* shutdown, a new one will take its place if needed to execute
* subsequent tasks.) Tasks are guaranteed to execute
* sequentially, and no more than one task will be active at any
* given time. Unlike the otherwise equivalent
* {@code newFixedThreadPool(1)} the returned executor is
* guaranteed not to be reconfigurable to use additional threads.
*
* @return the newly created single-threaded Executor
*/
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
從方法注釋上看驶社,和FixedThreadPool幾乎一樣企量,只不過該線程池的固定線程個數(shù)是1。這樣的好處是避免了線程間的同步問題亡电。
CachedThreadPool
/**
* Creates a thread pool that creates new threads as needed, but
* will reuse previously constructed threads when they are
* available. These pools will typically improve the performance
* of programs that execute many short-lived asynchronous tasks.
* Calls to {@code execute} will reuse previously constructed
* threads if available. If no existing thread is available, a new
* thread will be created and added to the pool. Threads that have
* not been used for sixty seconds are terminated and removed from
* the cache. Thus, a pool that remains idle for long enough will
* not consume any resources. Note that pools with similar
* properties but different details (for example, timeout parameters)
* may be created using {@link ThreadPoolExecutor} constructors.
*
* @return the newly created thread pool
*/
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
從方法注釋里看届巩,該線程池沒有核心線程,所有非核心線程數(shù)就是最大線程數(shù)份乒。由于最大線程數(shù)為無限大恕汇,所以每當(dāng)我們添加一個新任務(wù)進來的時候,如果線程池中有空閑的線程或辖,則由該空閑的線程執(zhí)行新任務(wù)瘾英,如果沒有空閑線程,則創(chuàng)建新線程來執(zhí)行任務(wù)颂暇。根據(jù)CachedThreadPool的特點方咆,我們可以在有大量任務(wù)請求的時候使用CachedThreadPool,因為當(dāng)CachedThreadPool中沒有新任務(wù)的時候蟀架,它里邊所有的線程都會因為超時(60秒)而被終止。
這里它使用了SynchronousQueue作為線程隊列榆骚。
特點:CachedTreadPool一個最大的優(yōu)勢是它可以根據(jù)程序的運行情況自動來調(diào)整線程池中的線程數(shù)量片拍。
ScheduledThreadPool
/**
* Creates a new {@code ScheduledThreadPoolExecutor} with the
* given core pool size.
*
* @param corePoolSize the number of threads to keep in the pool, even
* if they are idle, unless {@code allowCoreThreadTimeOut} is set
* @throws IllegalArgumentException if {@code corePoolSize < 0}
*/
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE,
DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
new DelayedWorkQueue());
}
從方法注釋里可以看到,它的核心線程數(shù)量是固定的(我們在構(gòu)造的時候傳入的)妓肢,但是非核心線程是無窮大捌省,當(dāng)非核心線程閑置時,則會被立即回收碉钠。
ScheduledExecutorService擴展了ExecutorService接口纲缓,提供時間排程的功能。
public interface ScheduledExecutorService extends ExecutorService {
/**
* Creates and executes a one-shot action that becomes enabled
* after the given delay.
* 翻譯:創(chuàng)建并執(zhí)行在給定延遲后啟用的一次性操作喊废。
*/
public ScheduledFuture<?> schedule(Runnable command,
long delay, TimeUnit unit);
/**
* Creates and executes a ScheduledFuture that becomes enabled after the
* given delay.
* 翻譯:創(chuàng)建并執(zhí)行在給定延遲后啟用的操作三妈。
*/
public <V> ScheduledFuture<V> schedule(Callable<V> callable,
long delay, TimeUnit unit);
/**
* Creates and executes a periodic action that becomes enabled first
* after the given initial delay, and subsequently with the given
* period; that is, executions will commence after
* 參數(shù)initialDelay:表示第一次延時的時間
* 參數(shù)period:表示任務(wù)執(zhí)行開始后距下一個任務(wù)開始執(zhí)行的時間間隔柑司。
*/
public ScheduledFuture<?> scheduleAtFixedRate(Runnable command,
long initialDelay,
long period,
TimeUnit unit);
/**
* Creates and executes a periodic action that becomes enabled first
* after the given initial delay, and subsequently with the
* given delay between the termination of one execution and the
* commencement of the next.
* 參數(shù)initialDelay:表示第一次延時的時間
* 參數(shù)delay:表示任務(wù)執(zhí)行結(jié)束后距下一個任務(wù)開始執(zhí)行的時間間隔
*/
public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command,
long initialDelay,
long delay,
TimeUnit unit);
}
情景代碼測試1:executorService.scheduleWithFixedDelay(new MyRunnable(5), 1, 3, TimeUnit.SECONDS);
class MyRunnable implements Runnable {
private int count;
final int[] i = {0};
public MyRunnable(int i) {
this.count = i;
}
@Override
public void run() {
try {
i[0] = i[0] + 1;
if (i[0] < count) {
Log.d(TAG, "開始第" + i[0] + "次打印");
Thread.sleep(1000);
Log.d(TAG, "結(jié)束第" + i[0] + "次打印");
} else {
Log.d(TAG, "停止延時執(zhí)行...");
executorService.shutdownNow();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
Log日志如下:
12-07 14:24:41.556 9263-9263 D/ThreadFragment: 開始延時執(zhí)行...
12-07 14:24:42.557 9263-9353 D/ThreadFragment: 開始第1次打印
12-07 14:24:43.559 9263-9353 D/ThreadFragment: 結(jié)束第1次打印
12-07 14:24:46.561 9263-9353 D/ThreadFragment: 開始第2次打印
12-07 14:24:47.562 9263-9353 D/ThreadFragment: 結(jié)束第2次打印
12-07 14:24:50.564 9263-9353 D/ThreadFragment: 開始第3次打印
12-07 14:24:51.567 9263-9353 D/ThreadFragment: 結(jié)束第3次打印
12-07 14:24:54.569 9263-9353 D/ThreadFragment: 開始第4次打印
12-07 14:24:55.570 9263-9353 D/ThreadFragment: 結(jié)束第4次打印
12-07 14:24:58.572 9263-9353 D/ThreadFragment: 停止延時執(zhí)行...
從log日志可以看出:
- 14:24:41到14:24:42是延遲1秒啟動的。
- 結(jié)束第1次打印時間與開始第2次打印時間是相隔3秒,驗證了每一次執(zhí)行終止和下一次執(zhí)行開始之間都存在給定的延遲delay么翰。
情景代碼測試2:executorService.scheduleAtFixedRate(new MyRunnable(5), 1, 3, TimeUnit.SECONDS);
Log日志如下(MyRunnable內(nèi)代碼一樣):
12-07 14:23:21.371 8012-8012 D/ThreadFragment: 開始延時執(zhí)行...
12-07 14:23:22.373 8012-8115 D/ThreadFragment: 開始第1次打印
12-07 14:23:23.374 8012-8115 D/ThreadFragment: 結(jié)束第1次打印
12-07 14:23:25.373 8012-8115 D/ThreadFragment: 開始第2次打印
12-07 14:23:26.375 8012-8115 D/ThreadFragment: 結(jié)束第2次打印
12-07 14:23:28.374 8012-8115 D/ThreadFragment: 開始第3次打印
12-07 14:23:29.375 8012-8115 D/ThreadFragment: 結(jié)束第3次打印
12-07 14:23:31.374 8012-8115 D/ThreadFragment: 開始第4次打印
12-07 14:23:32.376 8012-8115 D/ThreadFragment: 結(jié)束第4次打印
12-07 14:23:34.372 8012-8115 D/ThreadFragment: 停止延時執(zhí)行...
從log日志可以看出:
- 14:23:21到14:23:22是延遲1秒啟動的坦康。
- 開始第1次打印時間與開始第2次打印時間是period = 3秒。
注意:通過ScheduledExecutorService執(zhí)行的周期任務(wù),如果任務(wù)執(zhí)行過程中拋出了異常雷厂,那么過ScheduledExecutorService就會停止執(zhí)行任務(wù),且也不會再周期地執(zhí)行該任務(wù)了叠殷。所以你如果想保住任務(wù)都一直被周期執(zhí)行改鲫,那么catch一切可能的異常。