一谋减、為什么使用線程池
1.線程池吃好處
(1).對多個線程進行統(tǒng)一管理牡彻,避免資源競爭中出現(xiàn)問題
(2).(重點)對線程進行復用,線程在執(zhí)行完任務后不會立刻銷毀逃顶,而會等待另外的任務讨便,這樣就不會頻繁的創(chuàng)建、銷毀線程和調(diào)用GC
2.線程池適用的場景
(1)在項目中頻繁的開啟線程以政,需要多線程去處理不同的任務
(2)需要監(jiān)控線程的運行狀態(tài)
3.線程池運行規(guī)則
(1)如果線程池中的數(shù)量未達到核心線程的數(shù)量霸褒,則會直接啟動一個核心線程來執(zhí)行任務
(2)如果線程池中的數(shù)量已經(jīng)達到或超過核心線程數(shù),則任務會被插入到任務隊列中等待執(zhí)行
(3)如果(2)中的任務無法插入到任我隊列中盈蛮,由于任務隊列已滿废菱,這時候如果線程數(shù)量未達到線程池規(guī)定的最大值,則會啟動一個非核心線程來執(zhí)行
(4)如果(3)中線程數(shù)量已達到線程池最大值,則會拒絕執(zhí)行此任務殊轴,ThreadPoolExecutor會調(diào)用RejectedExecutionHandler的rejectedExecution方法通知調(diào)用者
二衰倦、線程池的實現(xiàn)ThreadPoolExecutor
1.構(gòu)造方法
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory)
參數(shù)的含義:
corePoolSize: 線程池中核心線程的數(shù)量。
maximumPoolSize: 線程池中最大線程數(shù)量旁理。
keepAliveTime: 非核心線程的超時時長樊零,當系統(tǒng)中非核心線程閑置時間超過keepAliveTime之后,則會被回收孽文。如果ThreadPoolExecutor的allowCoreThreadTimeOut屬性設(shè)置為true驻襟,則該參數(shù)也表示核心線程的超時時長。
unit: keepAliveTime這個參數(shù)的單位芋哭,有納秒沉衣、微秒、毫秒减牺、秒豌习、分、時拔疚、天等肥隆。
workQueue: 線程池中的任務隊列,該隊列主要用來存儲已經(jīng)被提交但是尚未執(zhí)行的任務草雕。存儲在這里的任務是由ThreadPoolExecutor的execute方法提交來的巷屿。
threadFactory: 為線程池提供創(chuàng)建新線程的功能固以,這個我們一般使用默認即可墩虹。
handler: 拒絕策略,當線程無法執(zhí)行新任務時(一般是由于線程池中的線程數(shù)量已經(jīng)達到最大數(shù)或者線程池關(guān)閉導致的)憨琳,默認情況下诫钓,當線程池無法處理新線程時,會拋出一個RejectedExecutionException篙螟。
2.兩個執(zhí)行的方法
(1)execute()方法
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
//獲得當前線程的生命周期對應的二進制狀態(tài)碼
int c = ctl.get();
//判斷當前線程數(shù)量是否小于核心線程數(shù)量,如果小于就直接創(chuàng)建核心線程執(zhí)行任務,創(chuàng)建成功直接跳出,失敗則接著往下走.
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
}
//判斷線程池是否為RUNNING狀態(tài),并且將任務添加至隊列中.
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
//審核下線程池的狀態(tài),如果不是RUNNING狀態(tài),直接移除隊列中
if (! isRunning(recheck) && remove(command))
reject(command);
//如果當前線程數(shù)量為0,則單獨創(chuàng)建線程,而不指定任務.
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
//如果不滿足上述條件,嘗試創(chuàng)建一個非核心線程來執(zhí)行任務,如果創(chuàng)建失敗,調(diào)用reject()方法.
else if (!addWorker(command, false))
reject(command);
}
(2)submit()方法
public <T> Future<T> submit(Callable<T> task) {
if (task == null) throw new NullPointerException();
RunnableFuture<T> ftask = newTaskFor(task);
//還是通過調(diào)用execute
execute(ftask);
//最后會將包裝好的Runable返回
return ftask;
}
//將Callable<T> 包裝進FutureTask中
protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {
return new FutureTask<T>(callable);
}
//可以看出FutureTask也是實現(xiàn)Runnable接口,因為RunableFuture本身就繼承了Runnabel接口
public class FutureTask<V> implements RunnableFuture<V> {
.......
}
public interface RunnableFuture<V> extends Runnable, Future<V> {
/**
* Sets this Future to the result of its computation
* unless it has been cancelled.
*/
void run();
}
(3)結(jié)論
從上面兩個方法的源碼我們可以分析出幾個結(jié)論菌湃,
submit()其實還是需要調(diào)用execute()去執(zhí)行任務的,不同是submit()將包裝好的任務進行了返回,他會返回一個Future對象遍略。
從execute()方法中,不難看出addWorker()方法, 是創(chuàng)建線程(核心線程,非核心線程)的主要方法,而reject()方法為線程創(chuàng)建失敗的回調(diào)惧所。
所以,通常情況下绪杏,在不需要線程執(zhí)行返回結(jié)果值時下愈,我們使用execute 方法。 而當我們需要返回值時蕾久,則使用submit方法势似,他會返回一個Future對象。Future不僅僅可以獲得一個結(jié)果,他還可以被取消履因,我們可以通過調(diào)用future的cancel()方法障簿,取消一個Future的執(zhí)行。 比如我們加入了一個線程栅迄,但是在這過程中我們又想中斷它站故,則可通過sumbit 來實現(xiàn)。
三毅舆、Android 中常用的幾種線程池
1.FixedThreadPool (可重用固定線程數(shù))
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
特點:參數(shù)為核心線程數(shù),只有核心線程污淋,無非核心線程寸爆,并且阻塞隊列無界
創(chuàng)建
//創(chuàng)建fixed線程池
final ExecutorService fixedThreadPool = Executors.newFixedThreadPool(5);
使用
/**
* fixed線程池
*/
mFixedPoolThread.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
for(int i = 0;i<30;i++){
final int finali = i;
Runnable runnable = new Runnable() {
@Override
public void run() {
try {
Thread.sleep(2000);
Log.d("Thread", "run: "+finali);
Log.d("當前線程:",Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
fixedThreadPool.execute(runnable);
}
}
});
結(jié)果為每2s打印5次任務,跟上面的基礎(chǔ)線程池類似
2.CachedThreadPool
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
CachedThreadPool中是沒有核心線程的魔种,但是它的最大線程數(shù)卻為Integer.MAX_VALUE,另外安拟,CachedThreadPool是有線程超時機制的,它的超時時間為60秒。
由于最大線程數(shù)為無限大顾瞻,所以每當添加一個新任務進來的時候王浴,如果線程池中有空閑的線程,則由該空閑的線程執(zhí)行新任務钞啸;如果沒有空閑線程,則創(chuàng)建新線程來執(zhí)行任務。
根據(jù)CachedThreadPool的特點蹬敲,在有大量耗時短的任務請求時,可使用CachedThreadPool,因為當CachedThreadPool中沒有新任務的時候阱扬,它里邊所有的線程都會因為60秒超時而被終止
創(chuàng)建
//創(chuàng)建Cached線程池
final ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
使用
/**
* cached線程池
*/
mCachedPoolThread.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
for(int i = 0;i<30;i++){
final int finali = i;
Runnable runnable = new Runnable() {
@Override
public void run() {
try {
Thread.sleep(2000);
Log.d("Thread", "run: "+finali);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
cachedThreadPool.execute(runnable);
}
}
});
結(jié)果:過2s后直接打印30個任務
3.SingleThreadPool(單個核線的fixed)
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE,
DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
new DelayedWorkQueue());
}
它的核心線程數(shù)量是固定的渠退,但是非核心線程無窮大。當非核心線程閑置時恰梢,則會被立即回收。
ScheduledThreadPool也是四個當中唯一一個具有定時定期執(zhí)行任務功能的線程池。它適合執(zhí)行一些周期性任務或者延時任務。
創(chuàng)建
//創(chuàng)建Single線程池
final ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
使用
/**
* single線程池
*/
mSinglePoolExecute.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
for(int i = 0;i<30;i++){
final int finali = i;x
Runnable runnable = new Runnable() {
@Override
public void run() {
try {
Thread.sleep(2000);
Log.d("Thread", "run: "+finali);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
singleThreadExecutor.execute(runnable);
}
}
});
結(jié)果:每2s打印一個任務定续,由于只有一個核心線程,當被占用時,其他的任務需要進入隊列等待峭状。
四、終止線程池中的某個線程
一般線程執(zhí)行完run方法之后逼争,線程就正常結(jié)束了,因此有如下幾種方式來實現(xiàn):
1.利用 Future 和 Callable
步驟:
實現(xiàn) Callable 接口
調(diào)用 pool.submit() 方法胆敞,返回 Future 對象
用 Future 對象來獲取線程的狀態(tài)杂伟。
private void cancelAThread() {
ExecutorService pool = Executors.newFixedThreadPool(2);
Callable<String> callable = new Callable<String>() {
@Override
public String call() throws Exception {
System.out.println("test");
return "true";
}
};
Future<String> f = pool.submit(callable);
System.out.println(f.isCancelled());
System.out.println(f.isDone());
f.cancel(true);
}
2.利用 volatile 關(guān)鍵字灵迫,設(shè)置退出flag, 用于終止線程
public class ThreadSafe extends Thread {
public volatile boolean isCancel = false;
public void run() {
while (!isCancel){
//TODO method();
}
}
}
3.interrupt()方法終止線程,并捕獲異常
public class ThreadSafe extends Thread {
@Override
public void run() {
while (!isInterrupted()){ //非阻塞過程中通過判斷中斷標志來退出
try{
//TODO method();
//阻塞過程捕獲中斷異常來退出
}catch(InterruptedException e){
e.printStackTrace();
break;//捕獲到異常之后嫌术,執(zhí)行break跳出循環(huán)。
}
}
}
}
參考: https://blog.csdn.net/qq_22393017/article/details/79356115
http://www.reibang.com/p/7b2da1d94b42