前言
?多任務處理在現(xiàn)實開發(fā)場景中已經(jīng)無處不在,通過多任務處理可以將計算機性能更大程度的發(fā)揮出來,避免處于空閑狀態(tài)浪費性能。
?對于計算量相同的任務,程序線程并發(fā)協(xié)調得越有條不紊,效率自然就會越高;反之,線程之間頻繁爭用數(shù)據(jù),互相阻塞甚至死鎖,將會大大降低程序的并發(fā)能力。
?因此我們有必要深入了解多線程開發(fā)。
1 概念
在說線程之前我們先來了解進程、線程、線程池概念。
-
進程(process)是指在系統(tǒng)中正在運行的一個應用程序。 -
線程(Thread)是比進程更輕量級的調度執(zhí)行單位,它可以把一個進程的資源分配和執(zhí)行調度分開。目前線程是CPU調度執(zhí)行的最基本單位。 -
線程池(ThreadPool)自動創(chuàng)建、銷毀線程的一個容器。
2 線程
?不建議直接new線程,因為存在以下幾點問題:
①不易復用,頻繁創(chuàng)建及銷毀開銷大
②特定場景不易使用,例如定時任務
?一個簡單的線程實現(xiàn)方式
fun doSomething() {
Thread{
//todo
}.start()
}
3 線程池
?線程池的優(yōu)點:
①可重用線程池中的線程,避免頻繁創(chuàng)建及銷毀帶來的性能開銷。
②可控制最大并發(fā)數(shù),避免大量線程之間因為相互搶占系統(tǒng)資源而導致阻塞。
③支持特定場景使用,例如定時任務。
?我們日常開發(fā)中所使用到的線程池在java.util.concurrent并發(fā)工具包下,繼承自Executor實現(xiàn)了ThreadPoolExecutor,ThreadPoolExecutor提供了一系列參數(shù)配置線程,如下所示:
/**
*
* @param corePoolSize 核心線程數(shù),默認情況一直存活
*
* @param maximumPoolSize 線程池最大線程數(shù)
*
* @param keepAliveTime 非核心線程閑置時的超時時長,超過這個時長就會被
* 回收。當allowCoreThreadTimeOut設置為true時作用于核心線程 。
*
* @param unit 指定keepAliveTime存活時常的單位,TimeUnit是個枚舉類,一般
* 有TimeUnit.SECONDS,TimeUnit.MINUTES
*
* @param workQueue 線程池任務隊列,通過execut提交Runnable執(zhí)行。
*
* @param threadFactory 當創(chuàng)建新線程時可以使用該工廠創(chuàng)建
*
* @throws 滿足以下條件拋出此異常 IllegalArgumentException
* {corePoolSize < 0}
* {keepAliveTime < 0}
* {maximumPoolSize <= 0}
* {maximumPoolSize < corePoolSize}
* @throws NullPointerException workQueue、threadFactory為null時拋出異常。
*/
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
threadFactory, defaultHandler);
}
?除以上參數(shù)外,還有一個參數(shù)RejectedExecutionHandler,當任務隊列已滿或者是無法成功執(zhí)行任務,就會調用RejectedExecutionHandler的rejectedExecution,默認拋出異常。ThreadPoolExecutor還為我們提供了CallerRunsPolicy、AbortPolicy、DiscardPolicy、DiscardOldestPolicy四種Handler,默認為AbortPolicy,因此是直接拋出異常。
public static class AbortPolicy implements RejectedExecutionHandler {
public AbortPolicy() { }
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
throw new RejectedExecutionException("Task " + r.toString() +
" rejected from " +
e.toString());
}
}
?一個簡單的線程池工具類創(chuàng)建如下:
private int CPU_Runtime = Runtime.getRuntime().availableProcessors() + 1;
public ThreadPoolExecutor threadPoolExecutor;
public ThreadPoolUtils() {
threadPoolExecutor = new ThreadPoolExecutor(CPU_Runtime, CPU_Runtime,
1, TimeUnit.MINUTES,
new LinkedBlockingQueue<Runnable>(128),
new MyThreadFactory("test"));
}
/**
* 記錄是線程池中第幾個線程
*/
private static class MyThreadFactory implements ThreadFactory {
private final String name;
private final AtomicInteger mCount = new AtomicInteger(1);
MyThreadFactory(String name) {
this.name = name;
}
@Override
public Thread newThread(@NonNull Runnable r) {
return new Thread(r, name + "-" + mCount.getAndIncrement() + "-");
}
}
?根據(jù)實際開發(fā)需求做了以上這些配置,配置參數(shù)規(guī)格如下:
- 核心線程數(shù)為CPU核心數(shù)+1
- 非核心線程數(shù)與核心線程數(shù)相同
- 非核心線程超時時間為1分鐘
- 任務隊列容量為128
- 使用工廠模式創(chuàng)建線程,便于查看當前執(zhí)行的線程相關信息。
4 常用的4種線程池
?java還為我們實現(xiàn)了配置了4種常用的線程池,newFixedThreadPool、newCachedThreadPool、newScheduledThreadPool、newSingleThreadExecutor,它們都直接或間接配置了ThreadPoolExecutor。
4.1 newFixedThreadPool
?固定線程數(shù)的線程池,當處于空閑狀態(tài)不會被回收。因為最大線程數(shù)和核心線程相等,所以都是核心線程,處于空閑狀態(tài)不會被回收。
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
4.2 newCachedThreadPool
?數(shù)量不固定的線程池,因為最大線程數(shù)為Integer.MAX_VALUE,但是一般不會創(chuàng)建太多,因為創(chuàng)建線程開銷比較消耗性能。默認超時時長為60S。
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
4.3 newScheduledThreadPool
?創(chuàng)建固定數(shù)量的核心線程以及Integer.MAX_VALUE非核心線程,默認超時時長為10分鐘,一般用于執(zhí)行固定周期的重讀任務。
private static final long DEFAULT_KEEPALIVE_MILLIS = 10L;
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE,
DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
new DelayedWorkQueue());
}
4.4 newSingleThreadExecutor
?只有一個核心線程的線程池,它保證了所有的線程在此隊列中都是有序執(zhí)行的。一般用于統(tǒng)一外界任務,避免處理同步問題。
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
總結
?java.util.concurrent為我們提供了線程池的創(chuàng)建,方便我們管理線程,但隨著變成語言的發(fā)展,協(xié)程也出現(xiàn)在了我們日常開發(fā)中,無論是Java即將面世的遷程(Fiber)或者kotlin的協(xié)程(Coroutines),它們的創(chuàng)建與銷毀的開銷遠遠小于線程的創(chuàng)建,因此在使用kotlin開發(fā)Android時建議多使用協(xié)程進行多并發(fā)開發(fā)。