目錄介紹
- 1.遇到的問題和需求
- 1.1 遇到的問題有哪些
- 1.2 遇到的需求
- 1.3 多線程通過實(shí)現(xiàn)Runnable弊端
- 1.4 為什么要用線程池
- 2.封裝庫(kù)具有的功能
- 2.1 常用的功能
- 3.封裝庫(kù)的具體使用
- 3.1 一鍵集成
- 3.2 在application中初始化庫(kù)
- 3.3 最簡(jiǎn)單的runnable線程調(diào)用方式
- 3.4 最簡(jiǎn)單的異步回調(diào)
- 4.線程池封裝思路介紹
- 4.1 自定義Runnable和Callable類
- 4.2 添加回調(diào)接口Callback
- 4.3 創(chuàng)建線程池配置文件
- 4.4 創(chuàng)建java和android平臺(tái)消息器
- 4.5 創(chuàng)建PoolThread繼承Executor
- 4.6 使用builder模式獲取線程池對(duì)象
- 4.7 靈活創(chuàng)建線程池[重點(diǎn)]
- 4.8 啟動(dòng)線程池中的任務(wù)
- 4.9 如何關(guān)閉線程池操作
- 5.其他介紹
- 5.1 參考的開源案例
- 5.2 參考的博客
好消息
- 博客筆記大匯總【16年3月到至今】,包括Java基礎(chǔ)及深入知識(shí)點(diǎn),Android技術(shù)博客,Python學(xué)習(xí)筆記等等,還包括平時(shí)開發(fā)中遇到的bug匯總,當(dāng)然也在工作之余收集了大量的面試題,長(zhǎng)期更新維護(hù)并且修正,持續(xù)完善……開源的文件是markdown格式的!同時(shí)也開源了生活博客,從12年起,積累共計(jì)N篇[近100萬(wàn)字,陸續(xù)搬到網(wǎng)上],轉(zhuǎn)載請(qǐng)注明出處,謝謝!
- 鏈接地址:https://github.com/yangchong211/YCBlogs
- 如果覺得好,可以star一下,謝謝!當(dāng)然也歡迎提出建議,萬(wàn)事起于忽微,量變引起質(zhì)變!
0.前言介紹
- 輕量級(jí)線程池封裝庫(kù),支持線程執(zhí)行過程中狀態(tài)回調(diào)監(jiān)測(cè)(包含成功,失敗,異常等多種狀態(tài));支持創(chuàng)建異步任務(wù),并且可以設(shè)置線程的名稱,延遲執(zhí)行時(shí)間,線程優(yōu)先級(jí),回調(diào)callback等;可以根據(jù)自己需要?jiǎng)?chuàng)建自己需要的線程池,一共有四種;線程異常時(shí),可以打印異常日志,避免崩潰。
- 關(guān)于線程池,對(duì)于開發(fā)來(lái)說是十分重要,但是又有點(diǎn)難以理解或者運(yùn)用。關(guān)于寫線程池的博客網(wǎng)上已經(jīng)有很多了,但是一般很少有看到的實(shí)際案例或者封裝的庫(kù),許多博客也僅僅是介紹了線程池的概念,方法,或者部分源碼分析,那么為了方便管理線程任務(wù)操作,所以才想結(jié)合實(shí)際案例是不是更容易理解線程池,更多可以參考代碼。
線程池封裝庫(kù)GitHub鏈接:https://github.com/yangchong211/YCThreadPool
1.遇到的問題和需求
1.1 遇到的問題有哪些?
- 繼承Thread,或者實(shí)現(xiàn)接口Runnable來(lái)開啟一個(gè)子線程,無(wú)法準(zhǔn)確地知道線程什么時(shí)候執(zhí)行完成并獲得到線程執(zhí)行完成后返回的結(jié)果
- 當(dāng)線程出現(xiàn)異常的時(shí)候,如何避免導(dǎo)致崩潰問題?博客
1.2 遇到的需求
- 如何在實(shí)際開發(fā)中配置線程的優(yōu)先級(jí)
- 開啟一個(gè)線程,是否可以監(jiān)聽Runnable接口中run方法操作的過程,比如監(jiān)聽線程的狀態(tài)開始,成功,異常,完成等多種狀態(tài)。
- 開啟一個(gè)線程,是否可以監(jiān)聽Callable<T>接口中call()方法操作的過程,比如監(jiān)聽線程的狀態(tài)開始,錯(cuò)誤異常,完成等多種狀態(tài)。
1.3 多線程通過實(shí)現(xiàn)Runnable弊端
-
1.3.1 一般開啟線程的操作如下所示
new Thread(new Runnable() { @Override public void run() { //做一些任務(wù) } }).start(); - 創(chuàng)建了一個(gè)線程并執(zhí)行,它在任務(wù)結(jié)束后GC會(huì)自動(dòng)回收該線程。
- 在線程并發(fā)不多的程序中確實(shí)不錯(cuò),而假如這個(gè)程序有很多地方需要開啟大量線程來(lái)處理任務(wù),那么如果還是用上述的方式去創(chuàng)建線程處理的話,那么將導(dǎo)致系統(tǒng)的性能表現(xiàn)的非常糟糕。博客
- 1.3.2 主要的弊端有這些,可能總結(jié)并不全面
- 大量的線程創(chuàng)建、執(zhí)行和銷毀是非常耗cpu和內(nèi)存的,這樣將直接影響系統(tǒng)的吞吐量,導(dǎo)致性能急劇下降,如果內(nèi)存資源占用的比較多,還很可能造成OOM
- 大量的線程的創(chuàng)建和銷毀很容易導(dǎo)致GC頻繁的執(zhí)行,從而發(fā)生內(nèi)存抖動(dòng)現(xiàn)象,而發(fā)生了內(nèi)存抖動(dòng),對(duì)于移動(dòng)端來(lái)說,最大的影響就是造成界面卡頓
- 線程的創(chuàng)建和銷毀都需要時(shí)間,當(dāng)有大量的線程創(chuàng)建和銷毀時(shí),那么這些時(shí)間的消耗則比較明顯,將導(dǎo)致性能上的缺失
1.4 為什么要用線程池
- 使用線程池管理線程優(yōu)點(diǎn)
- ①降低系統(tǒng)資源消耗,通過重用已存在的線程,降低線程創(chuàng)建和銷毀造成的消耗;
- ②提高系統(tǒng)響應(yīng)速度,當(dāng)有任務(wù)到達(dá)時(shí),無(wú)需等待新線程的創(chuàng)建便能立即執(zhí)行;
- ③方便線程并發(fā)數(shù)的管控,線程若是無(wú)限制的創(chuàng)建,不僅會(huì)額外消耗大量系統(tǒng)資源,更是占用過多資源而阻塞系統(tǒng)或oom等狀況,從而降低系統(tǒng)的穩(wěn)定性。線程池能有效管控線程,統(tǒng)一分配、調(diào)優(yōu),提供資源使用率;
- ④更強(qiáng)大的功能,線程池提供了定時(shí)、定期以及可控線程數(shù)等功能的線程池,使用方便簡(jiǎn)單。
1.5 線程池執(zhí)行流程
- 大概的流程圖如下
- image
- 文字描述如下
- ①如果在線程池中的線程數(shù)量沒有達(dá)到核心的線程數(shù)量,這時(shí)候就回啟動(dòng)一個(gè)核心線程來(lái)執(zhí)行任務(wù)。
- ②如果線程池中的線程數(shù)量已經(jīng)超過核心線程數(shù),這時(shí)候任務(wù)就會(huì)被插入到任務(wù)隊(duì)列中排隊(duì)等待執(zhí)行。
- ③由于任務(wù)隊(duì)列已滿,無(wú)法將任務(wù)插入到任務(wù)隊(duì)列中。這個(gè)時(shí)候如果線程池中的線程數(shù)量沒有達(dá)到線程池所設(shè)定的最大值,那么這時(shí)候就會(huì)立即啟動(dòng)一個(gè)非核心線程來(lái)執(zhí)行任務(wù)。
- ④如果線程池中的數(shù)量達(dá)到了所規(guī)定的最大值,那么就會(huì)拒絕執(zhí)行此任務(wù),這時(shí)候就會(huì)調(diào)用RejectedExecutionHandler中的rejectedExecution方法來(lái)通知調(diào)用者。博客
2.封裝庫(kù)具有的功能
2.1 常用的功能
- 支持線程執(zhí)行過程中狀態(tài)回調(diào)監(jiān)測(cè)(包含成功,失敗,異常等多種狀態(tài))
- 支持線程異常檢測(cè),并且可以打印異常日志
- 支持設(shè)置線程屬性,比如名稱,延時(shí)時(shí)長(zhǎng),優(yōu)先級(jí),callback
- 支持異步開啟線程任務(wù),支持監(jiān)聽異步回調(diào)監(jiān)聽
- 方便集成,方便使用,可以靈活選擇創(chuàng)建不同的線程池
3.封裝庫(kù)的具體使用
3.1 一鍵集成
- compile 'cn.yc:YCThreadPoolLib:1.3.0'
3.2 在application中初始化庫(kù)
- 代碼如下所示
public class App extends Application{ private static App instance; private PoolThread executor; public static synchronized App getInstance() { if (null == instance) { instance = new App(); } return instance; } public App(){} @Override public void onCreate() { super.onCreate(); instance = this; //初始化線程池管理器 initThreadPool(); } /** * 初始化線程池管理器 */ private void initThreadPool() { // 創(chuàng)建一個(gè)獨(dú)立的實(shí)例進(jìn)行使用 executor = PoolThread.ThreadBuilder .createFixed(5) .setPriority(Thread.MAX_PRIORITY) .setCallback(new LogCallback()) .build(); } /** * 獲取線程池管理器對(duì)象,統(tǒng)一的管理器維護(hù)所有的線程池 * @return executor對(duì)象 */ public PoolThread getExecutor(){ return executor; } } //自定義回調(diào)監(jiān)聽callback,可以全局設(shè)置,也可以單獨(dú)設(shè)置。都行 public class LogCallback implements ThreadCallback { private final String TAG = "LogCallback"; @Override public void onError(String name, Throwable t) { Log.e(TAG, "LogCallback"+"------onError"+"-----"+name+"----"+Thread.currentThread()+"----"+t.getMessage()); } @Override public void onCompleted(String name) { Log.e(TAG, "LogCallback"+"------onCompleted"+"-----"+name+"----"+Thread.currentThread()); } @Override public void onStart(String name) { Log.e(TAG, "LogCallback"+"------onStart"+"-----"+name+"----"+Thread.currentThread()); } }
3.3 最簡(jiǎn)單的runnable線程調(diào)用方式
- 關(guān)于設(shè)置callback回調(diào)監(jiān)聽,我這里在app初始化的時(shí)候設(shè)置了全局的logCallBack,所以這里沒有添加,對(duì)于每個(gè)單獨(dú)的執(zhí)行任務(wù),可以添加獨(dú)立callback。
PoolThread executor = App.getInstance().getExecutor(); executor.setName("最簡(jiǎn)單的線程調(diào)用方式"); executor.setDeliver(new AndroidDeliver()); executor.execute(new Runnable() { @Override public void run() { Log.e("MainActivity","最簡(jiǎn)單的線程調(diào)用方式"); } });
3.4 最簡(jiǎn)單的異步回調(diào)
- 如下所示
PoolThread executor = App.getInstance().getExecutor(); executor.setName("異步回調(diào)"); executor.setDelay(2,TimeUnit.MILLISECONDS); // 啟動(dòng)異步任務(wù) executor.async(new Callable<Login>(){ @Override public Login call() throws Exception { // 做一些操作 return null; } }, new AsyncCallback<Login>() { @Override public void onSuccess(Login user) { Log.e("AsyncCallback","成功"); } @Override public void onFailed(Throwable t) { Log.e("AsyncCallback","失敗"); } @Override public void onStart(String threadName) { Log.e("AsyncCallback","開始"); } });
4.線程池封裝思路介紹
4.1 自定義Runnable和自定義Callable類
-
4.1.1 首先看看Runnable和Callable接口代碼
public interface Runnable { public void run(); } public interface Callable<V> { V call() throws Exception; } 4.1.2 Runnable和Callable接口是干什么的
Runnable 從 JDK1.0 開始就有了,Callable 是在 JDK1.5 增加的。
Thread調(diào)用了Runnable接口中的方法用來(lái)在線程中執(zhí)行任務(wù)。Runnable 和 Callable 都代表那些要在不同的線程中執(zhí)行的任務(wù)。
Thread調(diào)用了Runnable接口中的方法用來(lái)在線程中執(zhí)行任務(wù)。博客
- 4.1.3 Runnable和Callable接口有何區(qū)別
- 它們的主要區(qū)別是 Callable 的 call() 方法可以返回值和拋出異常,而 Runnable 的 run() 方法沒有這些功能。Callable 可以返回裝載有計(jì)算結(jié)果的 Future 對(duì)象。博客
- 比較兩個(gè)接口,可以得出這樣結(jié)論:
- Callable 的任務(wù)執(zhí)行后可返回值,而 Runnable 的任務(wù)是不能返回值的
- call() 方法可以拋出異常,run()方法不可以的
- 運(yùn)行 Callable 任務(wù)可以拿到一個(gè) Future 對(duì)象,表示異步計(jì)算的結(jié)果。它提供了檢查計(jì)算是否完成的方法,以等待計(jì)算的完成,并檢索計(jì)算的結(jié)果。通過 Future 對(duì)象可以了解任務(wù)執(zhí)行情況,可取消任務(wù)的執(zhí)行,還可獲取執(zhí)行結(jié)果;
-
4.1.4 自定義Runnable包裝類,重點(diǎn)看run方法代碼邏輯
public final class RunnableWrapper implements Runnable { private String name; private CallbackDelegate delegate; private Runnable runnable; private Callable callable; public RunnableWrapper(ThreadConfigs configs) { this.name = configs.name; this.delegate = new CallbackDelegate(configs.callback, configs.deliver, configs.asyncCallback); } /** * 啟動(dòng)異步任務(wù),普通的 * @param runnable runnable * @return 對(duì)象 */ public RunnableWrapper setRunnable(Runnable runnable) { this.runnable = runnable; return this; } /** * 異步任務(wù),回調(diào)用于接收可調(diào)用任務(wù)的結(jié)果 * @param callable callable * @return 對(duì)象 */ public RunnableWrapper setCallable(Callable callable) { this.callable = callable; return this; } /** * 自定義xxRunnable繼承Runnable,實(shí)現(xiàn)run方法 * 詳細(xì)可以看我的GitHub:https://github.com/yangchong211 */ @Override public void run() { Thread current = Thread.currentThread(); ThreadToolUtils.resetThread(current, name, delegate); //開始 delegate.onStart(name); //注意需要判斷runnable,callable非空 // avoid NullPointException if (runnable != null) { runnable.run(); } else if (callable != null) { try { Object result = callable.call(); //監(jiān)聽成功 delegate.onSuccess(result); } catch (Exception e) { //監(jiān)聽異常 delegate.onError(name, e); } } //監(jiān)聽完成 delegate.onCompleted(name); } }
-
4.1.5 自定義Callable<T>包裝類,重點(diǎn)看call方法代碼邏輯
public final class CallableWrapper<T> implements Callable<T> { private String name; private ThreadCallback callback; private Callable<T> proxy; /** * 構(gòu)造方法 * @param configs thread配置,主要參數(shù)有:線程name,延遲time,回調(diào)callback,異步callback * @param proxy 線程優(yōu)先級(jí) */ public CallableWrapper(ThreadConfigs configs, Callable<T> proxy) { this.name = configs.name; this.proxy = proxy; this.callback = new CallbackDelegate(configs.callback, configs.deliver, configs.asyncCallback); } /** * 詳細(xì)可以看我的GitHub:https://github.com/yangchong211 * 自定義Callable繼承Callable<T>類,Callable 是在 JDK1.5 增加的。 * Callable 的 call() 方法可以返回值和拋出異常 * @return 泛型 * @throws Exception 異常 */ @Override public T call() { ThreadToolUtils.resetThread(Thread.currentThread(),name,callback); if (callback != null) { //開始 callback.onStart(name); } T t = null; try { t = proxy == null ? null : proxy.call(); } catch (Exception e) { e.printStackTrace(); //異常錯(cuò)誤 if(callback!=null){ callback.onError(name,e); } }finally { //完成 if (callback != null) { callback.onCompleted(name); } } return t; } }
4. 添加回調(diào)接口AsyncCallback和ThreadCallback
- 注意,這個(gè)寫的自定義callback,需要添加多種狀態(tài),你可以自定義其他狀態(tài)??赐炅诉@里再回過頭看看RunnableWrapper中run方法和CallableWrapper中call方法的邏輯。博客
- 4.0 為什么要這樣設(shè)計(jì)
- 它可以讓程序員準(zhǔn)確地知道線程什么時(shí)候執(zhí)行完成并獲得到線程執(zhí)行完成后返回的結(jié)果。
- AsyncCallback,在這個(gè)類中,可以看到三種狀態(tài)[這個(gè)是在自定義Runnable中的run方法中實(shí)現(xiàn)],并且成功時(shí)可以攜帶結(jié)果,在異常時(shí)還可以打印異常日志。
- ThreadCallback,在這個(gè)類中,可以看到三種狀態(tài)[這個(gè)是在自定義Callable<T>中的call方法中實(shí)現(xiàn)],并且在異常時(shí)可以打印異常日志,在開始和完成時(shí)可以打印線程名稱
-
4.1 AsyncCallback類代碼如下所示
/** * <pre> * @author 楊充 * blog http://www.itdecent.cn/p/53017c3fc75d * time * desc 異步callback回調(diào)接口 * revise * GitHub https://github.com/yangchong211 * </pre> */ public interface AsyncCallback<T> { /** * 成功時(shí)調(diào)用 * @param t 泛型 */ void onSuccess(T t); /** * 異常時(shí)調(diào)用 * @param t 異常 */ void onFailed(Throwable t); /** * 通知用戶任務(wù)開始運(yùn)行 * @param threadName 正在運(yùn)行線程的名字 */ void onStart(String threadName); } -
4.2 ThreadCallback類代碼如下所示
/** * <pre> * @author: yangchong * blog : https://github.com/yangchong211 * time : * desc : 一個(gè)回調(diào)接口,用于通知用戶任務(wù)的狀態(tài)回調(diào)委托類 * 線程的名字可以自定義 * revise: * </pre> */ public interface ThreadCallback { /** * 當(dāng)線程發(fā)生錯(cuò)誤時(shí),將調(diào)用此方法。 * @param threadName 正在運(yùn)行線程的名字 * @param t 異常 */ void onError(String threadName, Throwable t); /** * 通知用戶知道它已經(jīng)完成 * @param threadName 正在運(yùn)行線程的名字 */ void onCompleted(String threadName); /** * 通知用戶任務(wù)開始運(yùn)行 * @param threadName 正在運(yùn)行線程的名字 */ void onStart(String threadName); }
4.3 創(chuàng)建線程池配置文件
- 為什么要添加配置文件,配置文件的作用主要是存儲(chǔ)當(dāng)前任務(wù)的某些配置,比如線程的名稱,回調(diào)callback等等這些參數(shù)。還可以用于參數(shù)的傳遞!
public final class ThreadConfigs { /** * 線程的名稱 * 通過setName方法設(shè)置 */ public String name; /** * 線程執(zhí)行延遲的時(shí)間 * 通過setDelay方法設(shè)置 */ public long delay; /** * 線程執(zhí)行者 * JAVA或者ANDROID */ public Executor deliver; /** * 用戶任務(wù)的狀態(tài)回調(diào)callback */ public ThreadCallback callback; /** * 異步callback回調(diào)callback */ public AsyncCallback asyncCallback; }
4.4 創(chuàng)建java平臺(tái)和android平臺(tái)消息器Executor
- 在android環(huán)境下,想一想callback回調(diào)類中的幾個(gè)方法,比如回調(diào)失敗,回調(diào)成功,或者回調(diào)完成,可能會(huì)做一些操作UI界面的操作邏輯,那么都知道子線程是不能更新UI的,所以必須放到主線程中操作。
- 但是在Java環(huán)境下,回調(diào)方法所運(yùn)行的線程與任務(wù)執(zhí)行線程其實(shí)可以保持一致。
- 因此,這里需要設(shè)置該消息器用來(lái)區(qū)別回調(diào)的邏輯。主要作用是指定回調(diào)任務(wù)需要運(yùn)行在什么線程之上。
-
4.4.1 android環(huán)境下
image
-
4.4.2 java環(huán)境下
image
-
4.4.3 如何判斷環(huán)境是java環(huán)境還是Android環(huán)境呢
public final class ThreadToolUtils { /** * 標(biāo)志:是否在android平臺(tái)上 */ public static boolean isAndroid; /* * 靜態(tài)代碼塊 * 判斷是否是android環(huán)境 * Class.forName(xxx.xx.xx) 返回的是一個(gè)類對(duì)象 * 首先要明白在java里面任何class都要裝載在虛擬機(jī)上才能運(yùn)行。 */ static { try { Class.forName("android.os.Build"); isAndroid = true; } catch (Exception e) { isAndroid = false; } } }
4.5 創(chuàng)建PoolThread繼承Executor
- 這里只是展示部分代碼,如果想看完整的代碼,可以直接看案例。博客
-
4.5.1 繼承Executor接口,并且實(shí)現(xiàn)execute方法
public final class PoolThread implements Executor{ /** * 啟動(dòng)任務(wù) * 這個(gè)是實(shí)現(xiàn)接口Executor中的execute方法 * 提交任務(wù)無(wú)返回值 * @param runnable task,注意添加非空注解 */ @Override public void execute (@NonNull Runnable runnable) { //獲取線程thread配置信息 ThreadConfigs configs = getLocalConfigs(); //設(shè)置runnable任務(wù) runnable = new RunnableWrapper(configs).setRunnable(runnable); //啟動(dòng)任務(wù) DelayTaskDispatcher.get().postDelay(configs.delay, pool, runnable); //重置線程Thread配置 resetLocalConfigs(); } /** * 當(dāng)啟動(dòng)任務(wù)或者發(fā)射任務(wù)之后需要調(diào)用該方法 * 重置本地配置,置null */ private synchronized void resetLocalConfigs() { local.set(null); } /** * 注意需要用synchronized修飾,解決了多線程的安全問題 * 獲取本地配置參數(shù) * @return */ private synchronized ThreadConfigs getLocalConfigs() { ThreadConfigs configs = local.get(); if (configs == null) { configs = new ThreadConfigs(); configs.name = defName; configs.callback = defCallback; configs.deliver = defDeliver; local.set(configs); } return configs; } }
4.6 使用builder模式獲取線程池對(duì)象
- 4.6.1 看下builder模式下代碼
- 如果還不是很熟悉builder模式,歡迎閱讀我的另外一篇文章之——設(shè)計(jì)模式之二:Builder模式:http://www.itdecent.cn/p/246b01ca84c2
- 也可以看Android源碼設(shè)計(jì)模式這本書,很不錯(cuò)
- 直接列出代碼,如下所示:
public final class PoolThread implements Executor{ //省略部分代碼…… public static class ThreadBuilder { final static int TYPE_CACHE = 0; final static int TYPE_FIXED = 1; final static int TYPE_SINGLE = 2; final static int TYPE_SCHEDULED = 3; int type; int size; int priority = Thread.NORM_PRIORITY; String name; ThreadCallback callback; Executor deliver; ExecutorService pool; private ThreadBuilder(int size, int type, ExecutorService pool) { this.size = Math.max(1, size); this.type = type; this.pool = pool; } /** * 通過Executors.newSingleThreadExecutor()創(chuàng)建線程池 * 內(nèi)部只有一個(gè)核心線程,所有任務(wù)進(jìn)來(lái)都要排隊(duì)按順序執(zhí)行 */ public static ThreadBuilder create(ExecutorService pool) { return new ThreadBuilder(1, TYPE_SINGLE, pool); } /** * 通過Executors.newCachedThreadPool()創(chuàng)建線程池 * 它是一個(gè)數(shù)量無(wú)限多的線程池,都是非核心線程,適合執(zhí)行大量耗時(shí)小的任務(wù) */ public static ThreadBuilder createCacheable() { return new ThreadBuilder(0, TYPE_CACHE, null); } /** * 通過Executors.newFixedThreadPool()創(chuàng)建線程池 * 線程數(shù)量固定的線程池,全部為核心線程,響應(yīng)較快,不用擔(dān)心線程會(huì)被回收。 */ public static ThreadBuilder createFixed(int size) { return new ThreadBuilder(size, TYPE_FIXED, null); } /** * 通過Executors.newScheduledThreadPool()創(chuàng)建線程池 * 有數(shù)量固定的核心線程,且有數(shù)量無(wú)限多的非核心線程,適合用于執(zhí)行定時(shí)任務(wù)和固定周期的重復(fù)任務(wù) */ public static ThreadBuilder createScheduled(int size) { return new ThreadBuilder(size, TYPE_SCHEDULED, null); } /** * 通過Executors.newSingleThreadPool()創(chuàng)建線程池 * 內(nèi)部只有一個(gè)核心線程,所有任務(wù)進(jìn)來(lái)都要排隊(duì)按順序執(zhí)行 * 和create區(qū)別是size數(shù)量 */ public static ThreadBuilder createSingle() { return new ThreadBuilder(0, TYPE_SINGLE, null); } /** * 將默認(rèn)線程名設(shè)置為“已使用”。 */ public ThreadBuilder setName (@NonNull String name) { if (name.length()>0) { this.name = name; } return this; } /** * 將默認(rèn)線程優(yōu)先級(jí)設(shè)置為“已使用”。 */ public ThreadBuilder setPriority (int priority) { this.priority = priority; return this; } /** * 將默認(rèn)線程回調(diào)設(shè)置為“已使用”。 */ public ThreadBuilder setCallback (ThreadCallback callback) { this.callback = callback; return this; } /** * 設(shè)置默認(rèn)線程交付使用 */ public ThreadBuilder setDeliver(Executor deliver) { this.deliver = deliver; return this; } /** * 創(chuàng)建用于某些配置的線程管理器。 * @return 對(duì)象 */ public PoolThread build () { //最大值 priority = Math.max(Thread.MIN_PRIORITY, priority); //最小值 priority = Math.min(Thread.MAX_PRIORITY, priority); size = Math.max(1, size); if (name==null || name.length()==0) { // 如果沒有設(shè)置名字,那么就使用下面默認(rèn)的線程名稱 switch (type) { case TYPE_CACHE: name = "CACHE"; break; case TYPE_FIXED: name = "FIXED"; break; case TYPE_SINGLE: name = "SINGLE"; break; default: name = "POOL_THREAD"; break; } } if (deliver == null) { if (ThreadToolUtils.isAndroid) { deliver = AndroidDeliver.getInstance(); } else { deliver = JavaDeliver.getInstance(); } } return new PoolThread(type, size, priority, name, callback, deliver, pool); } } }
-
4.6.2 添加設(shè)置thread配置信息的方法
/** * 為當(dāng)前的任務(wù)設(shè)置線程名。 * @param name 線程名字 * @return PoolThread */ public PoolThread setName(String name) { getLocalConfigs().name = name; return this; } /** * 設(shè)置當(dāng)前任務(wù)的線程回調(diào),如果未設(shè)置,則應(yīng)使用默認(rèn)回調(diào)。 * @param callback 線程回調(diào) * @return PoolThread */ public PoolThread setCallback (ThreadCallback callback) { getLocalConfigs().callback = callback; return this; } /** * 設(shè)置當(dāng)前任務(wù)的延遲時(shí)間. * 只有當(dāng)您的線程池創(chuàng)建時(shí),它才會(huì)產(chǎn)生效果。 * @param time 時(shí)長(zhǎng) * @param unit time unit * @return PoolThread */ public PoolThread setDelay (long time, TimeUnit unit) { long delay = unit.toMillis(time); getLocalConfigs().delay = Math.max(0, delay); return this; } /** * 設(shè)置當(dāng)前任務(wù)的線程傳遞。如果未設(shè)置,則應(yīng)使用默認(rèn)傳遞。 * @param deliver thread deliver * @return PoolThread */ public PoolThread setDeliver(Executor deliver){ getLocalConfigs().deliver = deliver; return this; }
- 4.6.3 看下builder模式下創(chuàng)建對(duì)象的代碼
- 通過調(diào)用ThreadBuilder類中的build()方法創(chuàng)建屬于自己的線程池。
- 最后通過new PoolThread(type, size, priority, name, callback, deliver, pool)創(chuàng)建對(duì)象,并且作為返回值返回。
- 然后再來(lái)看看PoolThread方法,這部分看目錄4.7部分介紹。博客
4.7 靈活創(chuàng)建線程池[重點(diǎn)]
- 4.7.1 創(chuàng)建線程池的五種方法
- 通過Executors的工廠方法獲取這五種線程池
- 通過Executors的工廠方法來(lái)創(chuàng)建線程池極其簡(jiǎn)便,其實(shí)它的內(nèi)部還是通過new ThreadPoolExecutor(…)的方式創(chuàng)建線程池的,具體可以看看源碼,這里省略呢……
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(5); ExecutorService singleThreadPool = Executors.newSingleThreadExecutor(); ExecutorService cachedThreadPool = Executors.newCachedThreadPool(); ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5); ScheduledExecutorService singleThreadScheduledPool = Executors.newSingleThreadScheduledExecutor();
- 4.7.2 靈活創(chuàng)建不同類型線程池
- 設(shè)計(jì)的時(shí)候,希望能夠選擇性地創(chuàng)建自己想要的線程池,并且動(dòng)態(tài)設(shè)置線程的數(shù)量,還可以設(shè)置線程優(yōu)先級(jí)。
-
4.7.2.1 創(chuàng)建不同類型線程池代碼如下所示:
/** * 創(chuàng)建線程池,目前支持以下四種 * @param type 類型 * @param size 數(shù)量size * @param priority 優(yōu)先級(jí) * @return */ private ExecutorService createPool(int type, int size, int priority) { switch (type) { case Builder.TYPE_CACHE: //它是一個(gè)數(shù)量無(wú)限多的線程池,都是非核心線程,適合執(zhí)行大量耗時(shí)小的任務(wù) return Executors.newCachedThreadPool(new DefaultFactory(priority)); case Builder.TYPE_FIXED: //線程數(shù)量固定的線程池,全部為核心線程,響應(yīng)較快,不用擔(dān)心線程會(huì)被回收。 return Executors.newFixedThreadPool(size, new DefaultFactory(priority)); case Builder.TYPE_SCHEDULED: //有數(shù)量固定的核心線程,且有數(shù)量無(wú)限多的非核心線程,適合用于執(zhí)行定時(shí)任務(wù)和固定周期的重復(fù)任務(wù) return Executors.newScheduledThreadPool(size, new DefaultFactory(priority)); case Builder.TYPE_SINGLE: default: //內(nèi)部只有一個(gè)核心線程,所有任務(wù)進(jìn)來(lái)都要排隊(duì)按順序執(zhí)行 return Executors.newSingleThreadExecutor(new DefaultFactory(priority)); } }
- 4.7.2.1 了解一下ThreadFactory接口作用
- 關(guān)于ThreadFactory接口的源代碼如下所示:
- 以看到ThreadFactory中,只有一個(gè)newThread方法,它負(fù)責(zé)接收一個(gè)Runnable對(duì)象,并將其封裝到Thread對(duì)象中,進(jìn)行執(zhí)行。
- 通過有道詞典對(duì)這個(gè)類的說明進(jìn)行翻譯是:根據(jù)需要?jiǎng)?chuàng)建新線程的對(duì)象。使用線程工廠可以消除對(duì){@link Thread#Thread(Runnable)新線程}的硬連接,從而使應(yīng)用程序能夠使用特殊的線程子類、優(yōu)先級(jí)等。
public interface ThreadFactory { /** * Constructs a new {@code Thread}. Implementations may also initialize * priority, name, daemon status, {@code ThreadGroup}, etc. * * @param r a runnable to be executed by new thread instance * @return constructed thread, or {@code null} if the request to * create a thread is rejected */ Thread newThread(Runnable r); }
- 4.7.2.3 創(chuàng)建默認(rèn)MyThreadFactory繼承ThreadFactory
- 創(chuàng)建一個(gè)默認(rèn)的MyThreadFactory,并且這個(gè)類繼承ThreadFactory,實(shí)現(xiàn)接口中的newThread方法。然后在newThread方法中創(chuàng)建線程,并且設(shè)置線程優(yōu)先級(jí)。
- 創(chuàng)建一個(gè)優(yōu)先級(jí)線程池非常有用,它可以在線程池中線程數(shù)量不足或系統(tǒng)資源緊張時(shí),優(yōu)先處理我們想要先處理的任務(wù),而優(yōu)先級(jí)低的則放到后面再處理,這極大改善了系統(tǒng)默認(rèn)線程池以FIFO方式處理任務(wù)的不靈活。
- 代碼如下所示
public class MyThreadFactory implements ThreadFactory { private int priority; public MyThreadFactory(int priority) { this.priority = priority; } @Override public Thread newThread(@NonNull Runnable runnable) { Thread thread = new Thread(runnable); thread.setPriority(priority); return thread; } }
4.8 啟動(dòng)線程池中的任務(wù)
- 具體邏輯看DelayTaskExecutor中的postDelay方法
/** * 啟動(dòng) * @param delay 延遲執(zhí)行的時(shí)間,注意默認(rèn)單位是TimeUnit.MILLISECONDS * @param pool pool線程池 * @param task runnable */ void postDelay(long delay, final ExecutorService pool, final Runnable task) { if (delay == 0) { //如果時(shí)間是0,那么普通開啟 pool.execute(task); return; } //延時(shí)操作 dispatcher.schedule(new Runnable() { @Override public void run() { //在將來(lái)的某個(gè)時(shí)間執(zhí)行給定的命令。該命令可以在新線程、池線程或調(diào)用線程中執(zhí)行 pool.execute(task); } }, delay, TimeUnit.MILLISECONDS); }
4.9 如何關(guān)閉線程池操作
- 代碼如下所示
/** * 關(guān)閉線程池操作 */ public void stop(){ try { // shutdown只是起到通知的作用 // 只調(diào)用shutdown方法結(jié)束線程池是不夠的 pool.shutdown(); // (所有的任務(wù)都結(jié)束的時(shí)候,返回TRUE) if(!pool.awaitTermination(0, TimeUnit.MILLISECONDS)){ // 超時(shí)的時(shí)候向線程池中所有的線程發(fā)出中斷(interrupted)。 pool.shutdownNow(); } } catch (InterruptedException e) { // awaitTermination方法被中斷的時(shí)候也中止線程池中全部的線程的執(zhí)行。 e.printStackTrace(); } finally { pool.shutdownNow(); } }
5.其他介紹
5.1 參考的開源案例
- Thread,Java多線程核心技術(shù):https://github.com/boostorg/thread
- threadpool4j:https://github.com/aofeng/threadpool4j
- TickThreading:https://github.com/MinimallyCorrect/TickThreading
- MOBIN-F:https://github.com/MOBIN-F/Thread
- litesuits:https://github.com/litesuits/android-lite-async
5.2 參考的博客
- 自己封裝的簡(jiǎn)單ThreadFactory:https://blog.csdn.net/hbdatouerzi/article/details/73715668
- Android 多線程 線程池原理 封裝線程池:https://blog.csdn.net/xiangyunwan/article/details/72550948
- Android 自定義線程池的實(shí)戰(zhàn):https://www.cnblogs.com/zhaoyanjun/p/5761776.html
- android線程及線程池:http://www.itdecent.cn/p/d79dab197d5a
其他介紹
01.關(guān)于博客匯總鏈接
- 1.技術(shù)博客匯總
- 2.開源項(xiàng)目匯總
- 3.生活博客匯總
- 4.喜馬拉雅音頻匯總
- 5.其他匯總
02.關(guān)于我的博客
- github:https://github.com/yangchong211
- 知乎:https://www.zhihu.com/people/yczbj/activities
- 簡(jiǎn)書:http://www.itdecent.cn/u/b7b2c6ed9284
- csdn:http://my.csdn.net/m0_37700275
- 喜馬拉雅聽書:http://www.ximalaya.com/zhubo/71989305/
- 開源中國(guó):https://my.oschina.net/zbj1618/blog
- 泡在網(wǎng)上的日子:http://www.jcodecraeer.com/member/content_list.php?channelid=1
- 郵箱:yangchong211@163.com
- 阿里云博客:https://yq.aliyun.com/users/article?spm=5176.100- 239.headeruserinfo.3.dT4bcV
- segmentfault頭條:https://segmentfault.com/u/xiangjianyu/articles
- 掘金:https://juejin.im/user/5939433efe88c2006afa0c6e


