開篇:為什么要使用線程池?
在回答這個(gè)問題之前我們先想想為什么要使用多線程?
answer:由于計(jì)算機(jī)處理器的處理速度極大的超過了計(jì)算機(jī)存儲(chǔ)和通信子系統(tǒng)的處理速度憔购,在單線程情況下當(dāng)進(jìn)行磁盤IO操作或者進(jìn)行網(wǎng)絡(luò)請(qǐng)求時(shí)會(huì)導(dǎo)致處理器大部分時(shí)間處于空閑狀態(tài)薯蝎,這就造成了處理器資源的浪費(fèi)。同時(shí)Android中規(guī)定主線程只能進(jìn)行UI操作忱叭,對(duì)于耗時(shí)操作則需要放到子線程,否則可能會(huì)導(dǎo)致ANR今艺。所以為了盡量的“壓榨”處理器的效率韵丑,使得計(jì)算機(jī)同時(shí)能夠處理多個(gè)任務(wù),提高任務(wù)執(zhí)行效率虚缎,Java引入了多線程撵彻。
那為什么要使用線程池呢?
answer:對(duì)于每個(gè)jvm都有限定的內(nèi)存大小实牡,若無限制的去創(chuàng)建新線程輕者導(dǎo)致虛擬機(jī)頻繁的GC造成卡頓陌僵,重者直接oom導(dǎo)致虛擬機(jī)崩潰。并且計(jì)算機(jī)處理器的并發(fā)處理數(shù)量也是有限的创坞,超過一定數(shù)量的線程并不會(huì)加快處理器處理的效率碗短。所以為了維護(hù)Java虛擬機(jī)環(huán)境的綠色穩(wěn)定,充分利用處理器的速度题涨,統(tǒng)一管理線程偎谁,對(duì)線程按照一定規(guī)則進(jìn)行調(diào)度處理,Java引入了線程池纲堵。
so:如何去創(chuàng)建線程池巡雨?
我們可以直接使用ThreadPoolExecutor對(duì)象來創(chuàng)建線程池對(duì)象,也可以通過Java提供的四種線程類來快速的創(chuàng)建線程池對(duì)象席函。
代碼:通過ThreadPoolExecutor創(chuàng)建線程池
ExecutorService service = new ThreadPoolExecutor(3,3,
10, TimeUnit.SECONDS,new LinkedBlockingDeque<Runnable>());
/**
* Creates a new {@code ThreadPoolExecutor} with the given initial
* parameters and default thread factory.
*
* @param corePoolSize the number of threads to keep in the pool, even
* if they are idle, unless {@code allowCoreThreadTimeOut} is set
* @param maximumPoolSize the maximum number of threads to allow in the
* pool
* @param 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.
* @param unit the time unit for the {@code keepAliveTime} argument
* @param workQueue the queue to use for holding tasks before they are
* executed. This queue will hold only the {@code Runnable}
* tasks submitted by the {@code execute} method.
* @param 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:<br>
* {@code corePoolSize < 0}<br>
* {@code keepAliveTime < 0}<br>
* {@code maximumPoolSize <= 0}<br>
* {@code maximumPoolSize < corePoolSize}
* @throws NullPointerException if {@code workQueue}
* or {@code handler} is null
*/
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
RejectedExecutionHandler handler) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), handler);
}
corePoolSize:核心線程數(shù)铐望,默認(rèn)一直存在于線程池中,除非allowCoreThreadTimeOut設(shè)置為true茂附,則核心線程也會(huì)在空閑keepAliveTime之后回收蝌以。
maximumPoolSize:最大線程數(shù),包含核心線程和非核心線程何之,非核心線程在空閑keepAliveTime之后會(huì)回收跟畅。
keepAliveTime:空閑線程存活時(shí)間。
unit:時(shí)間單位(時(shí)分秒啥的)溶推。
workQueue:工作隊(duì)列徊件,所有需要執(zhí)行的Runnable對(duì)象都會(huì)被加入到此隊(duì)列中奸攻。
threadFactory: 創(chuàng)建線程的工廠類,默認(rèn)DefaultThreadFactory虱痕。
handler:RejectedExecutionHandler接口對(duì)象睹耐,默認(rèn)AbortPolicy,當(dāng)隊(duì)列達(dá)到限額時(shí)會(huì)阻塞然后執(zhí)行RejectedExecutionHandler中的rejectedExecution方法部翘。
代碼:通過Executors的靜態(tài)方法創(chuàng)建線程池對(duì)象硝训。
通過上述對(duì)ThreadPoolExecutor構(gòu)造器的分析,我們就能很好的理解下面四種線程池的特點(diǎn)新思。
1窖梁、FixedThreadPool,只有核心線程的線程池夹囚,空閑狀態(tài)下默認(rèn)不回收纵刘。代碼如下圖。
ExecutorService service = Executors.newFixedThreadPool(3);
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
2荸哟、CachedThreadPool,只有非核心線程的線程池假哎,線程空閑超過60秒將會(huì)被回收。代碼如下圖鞍历。
ExecutorService service = Executors.newCachedThreadPool();
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
3舵抹、ScheduledThreadPool,既有核心線程(由corePoolSize指定)又有非核心線程的線程池劣砍,非核心線程數(shù)的最大值為Integer.MAX_VALUE惧蛹。代碼如下圖。
ExecutorService service = Executors.newScheduledThreadPool(3);
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue());
}
4秆剪、SingleThreadExecutor,只有一個(gè)核心線程的線程池爵政。代碼如下仅讽。
ExecutorService service = Executors.newSingleThreadExecutor();
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
最后:使用線程池對(duì)象去執(zhí)行線程方法。
通過ExecutorService提供的execute钾挟、submit方法洁灵,將線程添加到線程池中進(jìn)行執(zhí)行處理。
代碼:通過execute(Runnable runnable)往線程池添加Runnable對(duì)象
service.execute(new Runnable() {
@Override
public void run() {
System.out.println("我在子線程執(zhí)行掺出,呵呵");
}
);
代碼:通過submit(Callable<T> callable)往線程池添加Callable對(duì)象
Future<Integer> task = service.submit(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
System.out.println("我也在子線程執(zhí)行徽千,呵呵");
return 66;
}
});
//將阻塞當(dāng)前線程直到task獲取到返回值66
task.get();
代碼:通過submit(Runnable runnable)往線程池添加Runnable對(duì)象
Future task1 = service.submit(new Runnable() {
@Override
public void run() {
System.out.println("我也在子線程執(zhí)行,呵呵");
}
});
//將阻塞當(dāng)前線程直到task獲取到返回值null
ask1.get();
代碼:通過submit(Runnable runnable, T result)往線程池添加Runnable對(duì)象并指定返回值result
Future<String> task2 = service.submit(new Runnable() {
@Override
public void run() {
System.out.println("我也在子線程執(zhí)行汤锨,呵呵");
}
}, "hello");
//將阻塞當(dāng)前線程直到task獲取到返回值hello
task2.get();