摘要:如果線程池中的線程數(shù)量已經(jīng)超過核心線程數(shù),這時(shí)候任務(wù)就會(huì)被插入到任務(wù)隊(duì)列中排隊(duì)等待執(zhí)行。如果線程池中的數(shù)量達(dá)到了所規(guī)定的最大值,那么就會(huì)拒絕執(zhí)行此任務(wù),這時(shí)候就會(huì)調(diào)用中的方法來通知調(diào)用者。
目錄介紹
1.遇到的問題和需求
1.1 遇到的問題有哪些
1.2 遇到的需求
1.3 多線程通過實(shí)現(xiàn)Runnable弊端
1.4 為什么要用線程池
2.封裝庫具有的功能
2.1 常用的功能
3.封裝庫的具體使用
3.1 一鍵集成
3.2 在application中初始化庫
3.3 最簡單的runnable線程調(diào)用方式
3.4 最簡單的異步回調(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模式獲取線程池對象
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)然也在工作之余收集了大量的面試題,長期更新維護(hù)并且修正,持續(xù)完善……開源的文件是markdown格式的!同時(shí)也開源了生活博客,從12年起,積累共計(jì)N篇[近100萬字,陸續(xù)搬到網(wǎng)上],轉(zhuǎn)載請注明出處,謝謝!
鏈接地址:https://github.com/yangchong2...
如果覺得好,可以star一下,謝謝!當(dāng)然也歡迎提出建議,萬事起于忽微,量變引起質(zhì)變!
0.前言介紹輕量級(jí)線程池封裝庫,支持線程執(zhí)行過程中狀態(tài)回調(diào)監(jiān)測(包含成功,失敗,異常等多種狀態(tài));支持創(chuàng)建異步任務(wù),并且可以設(shè)置線程的名稱,延遲執(zhí)行時(shí)間,線程優(yōu)先級(jí),回調(diào)callback等;可以根據(jù)自己需要?jiǎng)?chuàng)建自己需要的線程池,一共有四種;線程異常時(shí),可以打印異常日志,避免崩潰。
關(guān)于線程池,對于開發(fā)來說是十分重要,但是又有點(diǎn)難以理解或者運(yùn)用。關(guān)于寫線程池的博客網(wǎng)上已經(jīng)有很多了,但是一般很少有看到的實(shí)際案例或者封裝的庫,許多博客也僅僅是介紹了線程池的概念,方法,或者部分源碼分析,那么為了方便管理線程任務(wù)操作,所以才想結(jié)合實(shí)際案例是不是更容易理解線程池,更多可以參考代碼。
線程池封裝庫GitHub鏈接:https://github.com/yangchong2... 1.遇到的問題和需求 1.1 遇到的問題有哪些?繼承Thread,或者實(shí)現(xiàn)接口Runnable來開啟一個(gè)子線程,無法準(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
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è)程序有很多地方需要開啟大量線程來處理任務(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),對于移動(dòng)端來說,最大的影響就是造成界面卡頓
線程的創(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í),無需等待新線程的創(chuàng)建便能立即執(zhí)行;
③方便線程并發(fā)數(shù)的管控,線程若是無限制的創(chuàng)建,不僅會(huì)額外消耗大量系統(tǒng)資源,更是占用過多資源而阻塞系統(tǒng)或oom等狀況,從而降低系統(tǒng)的穩(wěn)定性。線程池能有效管控線程,統(tǒng)一分配、調(diào)優(yōu),提供資源使用率;
④更強(qiáng)大的功能,線程池提供了定時(shí)、定期以及可控線程數(shù)等功能的線程池,使用方便簡單。
1.5 線程池執(zhí)行流程
大概的流程圖如下
文字描述如下
①如果在線程池中的線程數(shù)量沒有達(dá)到核心的線程數(shù)量,這時(shí)候就回啟動(dòng)一個(gè)核心線程來執(zhí)行任務(wù)。
②如果線程池中的線程數(shù)量已經(jīng)超過核心線程數(shù),這時(shí)候任務(wù)就會(huì)被插入到任務(wù)隊(duì)列中排隊(duì)等待執(zhí)行。
③由于任務(wù)隊(duì)列已滿,無法將任務(wù)插入到任務(wù)隊(duì)列中。這個(gè)時(shí)候如果線程池中的線程數(shù)量沒有達(dá)到線程池所設(shè)定的最大值,那么這時(shí)候就會(huì)立即啟動(dòng)一個(gè)非核心線程來執(zhí)行任務(wù)。
④如果線程池中的數(shù)量達(dá)到了所規(guī)定的最大值,那么就會(huì)拒絕執(zhí)行此任務(wù),這時(shí)候就會(huì)調(diào)用RejectedExecutionHandler中的rejectedExecution方法來通知調(diào)用者。博客
2.封裝庫具有的功能 2.1 常用的功能支持線程執(zhí)行過程中狀態(tài)回調(diào)監(jiān)測(包含成功,失敗,異常等多種狀態(tài))
支持線程異常檢測,并且可以打印異常日志
支持設(shè)置線程屬性,比如名稱,延時(shí)時(shí)長,優(yōu)先級(jí),callback
支持異步開啟線程任務(wù),支持監(jiān)聽異步回調(diào)監(jiān)聽
方便集成,方便使用,可以靈活選擇創(chuàng)建不同的線程池
3.封裝庫的具體使用 3.1 一鍵集成compile "cn.yc:YCThreadPoolLib:1.3.0"
3.2 在application中初始化庫
代碼如下所示
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(); } /** * 獲取線程池管理器對象,統(tǒng)一的管理器維護(hù)所有的線程池 * @return executor對象 */ public PoolThread getExecutor(){ return executor; } } //自定義回調(diào)監(jiān)聽callback,可以全局設(shè)置,也可以多帶帶設(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 最簡單的runnable線程調(diào)用方式
關(guān)于設(shè)置callback回調(diào)監(jiān)聽,我這里在app初始化的時(shí)候設(shè)置了全局的logCallBack,所以這里沒有添加,對于每個(gè)多帶帶的執(zhí)行任務(wù),可以添加獨(dú)立callback。
PoolThread executor = App.getInstance().getExecutor(); executor.setName("最簡單的線程調(diào)用方式"); executor.setDeliver(new AndroidDeliver()); executor.execute(new Runnable() { @Override public void run() { Log.e("MainActivity","最簡單的線程調(diào)用方式"); } });3.4 最簡單的異步回調(diào)
如下所示
PoolThread executor = App.getInstance().getExecutor(); executor.setName("異步回調(diào)"); executor.setDelay(2,TimeUnit.MILLISECONDS); // 啟動(dòng)異步任務(wù) executor.async(new Callable4.線程池封裝思路介紹 4.1 自定義Runnable和自定義Callable類(){ @Override public Login call() throws Exception { // 做一些操作 return null; } }, new AsyncCallback () { @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.1.1 首先看看Runnable和Callable接口代碼
public interface Runnable { public void run(); } public interface Callable{ V call() throws Exception; }
4.1.2 Runnable和Callable接口是干什么的
Runnable 從 JDK1.0 開始就有了,Callable 是在 JDK1.5 增加的。
Thread調(diào)用了Runnable接口中的方法用來在線程中執(zhí)行任務(wù)。Runnable 和 Callable 都代表那些要在不同的線程中執(zhí)行的任務(wù)。
Thread調(diào)用了Runnable接口中的方法用來在線程中執(zhí)行任務(wù)。博客
4.1.3 Runnable和Callable接口有何區(qū)別
它們的主要區(qū)別是 Callable 的 call() 方法可以返回值和拋出異常,而 Runnable 的 run() 方法沒有這些功能。Callable 可以返回裝載有計(jì)算結(jié)果的 Future 對象。博客
比較兩個(gè)接口,可以得出這樣結(jié)論:
Callable 的任務(wù)執(zhí)行后可返回值,而 Runnable 的任務(wù)是不能返回值的
call() 方法可以拋出異常,run()方法不可以的
運(yùn)行 Callable 任務(wù)可以拿到一個(gè) Future 對象,表示異步計(jì)算的結(jié)果。它提供了檢查計(jì)算是否完成的方法,以等待計(jì)算的完成,并檢索計(jì)算的結(jié)果。通過 Future 對象可以了解任務(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
*/ public RunnableWrapper setRunnable(Runnable runnable) { this.runnable = runnable; return this; } /** * 異步任務(wù),回調(diào)用于接收可調(diào)用任務(wù)的結(jié)果 * @param callable callable * @return 對象 */ 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
public final class CallableWrapperimplements Callable { private String name; private ThreadCallback callback; private Callable proxy; /** * 構(gòu)造方法 * @param configs thread配置,主要參數(shù)有:線程name,延遲time,回調(diào)callback,異步callback
*/ public CallableWrapper(ThreadConfigs configs, Callable4. 添加回調(diào)接口AsyncCallback和ThreadCallbackproxy) { 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 類,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; } } ```
注意,這個(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
4.1 AsyncCallback類代碼如下所示
/** ** @author 楊充 * blog https://www.jianshu.com/p/53017c3fc75d * time * desc 異步callback回調(diào)接口 * revise * GitHub https://github.com/yangchong211*/ public interface AsyncCallback{ /** * 成功時(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類代碼如下所示
/** ** @author: yangchong * blog : https://github.com/yangchong211 * time : * desc : 一個(gè)回調(diào)接口,用于通知用戶任務(wù)的狀態(tài)回調(diào)委托類 * 線程的名字可以自定義 * revise:*/ 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 { /** * 線程的名稱*/ 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è)置該消息器用來區(qū)別回調(diào)的邏輯。主要作用是指定回調(diào)任務(wù)需要運(yùn)行在什么線程之上。
4.4.1 android環(huán)境下
4.4.2 java環(huán)境下
4.4.3 如何判斷環(huán)境是java環(huán)境還是Android環(huán)境呢
public final class ThreadToolUtils { /***/ public static boolean isAndroid; /* * 靜態(tài)代碼塊 * 判斷是否是android環(huán)境 * Class.forName(xxx.xx.xx) 返回的是一個(gè)類對象 * 首先要明白在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ù)無返回值*/ @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模式獲取線程池對象4.6.1 看下builder模式下代碼
如果還不是很熟悉builder模式,歡迎閱讀我的另外一篇文章之——設(shè)計(jì)模式之二:Builder模式:https://www.jianshu.com/p/246...
也可以看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)建線程池*/ public static ThreadBuilder create(ExecutorService pool) { return new ThreadBuilder(1, TYPE_SINGLE, pool); } /** * 通過Executors.newCachedThreadPool()創(chuàng)建線程池 * 它是一個(gè)數(shù)量無限多的線程池,都是非核心線程,適合執(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ù)量無限多的非核心線程,適合用于執(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)來都要排隊(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 對象 */ 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 線程名字*/ 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í)長 * @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)建對象的代碼
通過調(diào)用ThreadBuilder類中的build()方法創(chuàng)建屬于自己的線程池。
最后通過new PoolThread(type, size, priority, name, callback, deliver, pool)創(chuàng)建對象,并且作為返回值返回。
然后再來看看PoolThread方法,這部分看目錄4.7部分介紹。博客
4.7 靈活創(chuàng)建線程池[重點(diǎn)]4.7.1 創(chuàng)建線程池的五種方法
通過Executors的工廠方法獲取這五種線程池
通過Executors的工廠方法來創(chuàng)建線程池極其簡便,其實(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í)*/ private ExecutorService createPool(int type, int size, int priority) { switch (type) { case Builder.TYPE_CACHE: //它是一個(gè)數(shù)量無限多的線程池,都是非核心線程,適合執(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ù)量無限多的非核心線程,適合用于執(zhí)行定時(shí)任務(wù)和固定周期的重復(fù)任務(wù) return Executors.newScheduledThreadPool(size, new DefaultFactory(priority)); case Builder.TYPE_SINGLE: default: //內(nèi)部只有一個(gè)核心線程,所有任務(wù)進(jìn)來都要排隊(duì)按順序執(zhí)行 return Executors.newSingleThreadExecutor(new DefaultFactory(priority)); } } ```4.7.2.1 了解一下ThreadFactory接口作用
關(guān)于ThreadFactory接口的源代碼如下所示:
以看到ThreadFactory中,只有一個(gè)newThread方法,它負(fù)責(zé)接收一個(gè)Runnable對象,并將其封裝到Thread對象中,進(jìn)行執(zhí)行。
通過有道詞典對這個(gè)類的說明進(jìn)行翻譯是:根據(jù)需要?jiǎng)?chuàng)建新線程的對象。使用線程工廠可以消除對{@link Thread#Thread(Runnable)新線程}的硬連接,從而使應(yīng)用程序能夠使用特殊的線程子類、優(yōu)先級(jí)等。
public interface ThreadFactory { /** * Constructs a new {@code Thread}. Implementations may also initialize* * @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線程池*/ 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() { //在將來的某個(gè)時(shí)間執(zhí)行給定的命令。該命令可以在新線程、池線程或調(diào)用線程中執(zhí)行 pool.execute(task); } }, delay, TimeUnit.MILLISECONDS); } ```4.9 如何關(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/t...
threadpool4j:https://github.com/aofeng/thr...
TickThreading:https://github.com/MinimallyC...
MOBIN-F:https://github.com/MOBIN-F/Th...
litesuits:https://github.com/litesuits/...
5.2 參考的博客自己封裝的簡單ThreadFactory:https://blog.csdn.net/hbdatou...
Android 多線程 線程池原理 封裝線程池:https://blog.csdn.net/xiangyu...
Android 自定義線程池的實(shí)戰(zhàn):https://www.cnblogs.com/zhaoy...
android線程及線程池:https://www.jianshu.com/p/d79...
其他介紹 01.關(guān)于博客匯總鏈接1.技術(shù)博客匯總
2.開源項(xiàng)目匯總
3.生活博客匯總
4.喜馬拉雅音頻匯總
5.其他匯總
02.關(guān)于我的博客github:https://github.com/yangchong211
知乎:https://www.zhihu.com/people/...
簡書:http://www.jianshu.com/u/b7b2...
csdn:http://my.csdn.net/m0_37700275
喜馬拉雅聽書:http://www.ximalaya.com/zhubo...
開源中國:https://my.oschina.net/zbj161...
泡在網(wǎng)上的日子:http://www.jcodecraeer.com/me...
阿里云博客:https://yq.aliyun.com/users/a... 239.headeruserinfo.3.dT4bcV
segmentfault頭條:https://segmentfault.com/u/xi...
掘金:https://juejin.im/user/593943...
線程池封裝庫GitHub鏈接:https://github.com/yangchong2...
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/74614.html
摘要:實(shí)現(xiàn)的原理權(quán)限機(jī)制與適配經(jīng)驗(yàn)掘金一概要已經(jīng)發(fā)布一段時(shí)間了,市面上很多應(yīng)用都已經(jīng)適配。希望對大家有所幫助,更短信驗(yàn)證就是那么簡單掘金又有半個(gè)月沒更新文章了,最近工作比較忙,一時(shí)沒時(shí)間寫,今天趁著清閑,趕緊補(bǔ)上一篇。。。。。 2017,你絕對想嘗試的新 Android 庫 - Android - 掘金1 BufferTextInputLayout 直接看效果圖:升序效果 ... 計(jì)算機(jī)程序...
摘要:錯(cuò)誤使用單利在開發(fā)中單例經(jīng)常需要持有對象,如果持有的對象生命周期與單例生命周期更短時(shí),或?qū)е聼o法被釋放回收,則有可能造成內(nèi)存泄漏。如果集合是類型的話,那內(nèi)存泄漏情況就會(huì)更為嚴(yán)重。 目錄介紹 1.OOM和崩潰優(yōu)化 1.1 OOM優(yōu)化 1.2 ANR優(yōu)化 1.3 Crash優(yōu)化 2.內(nèi)存泄漏優(yōu)化 2.0 動(dòng)畫資源未釋放 2.1 錯(cuò)誤使用單利 2.2 錯(cuò)誤使用靜態(tài)變量 2.3 ...
閱讀 931·2021-11-24 09:38
閱讀 947·2021-11-23 09:51
閱讀 2956·2021-11-16 11:44
閱讀 1785·2021-09-22 15:52
閱讀 1688·2021-09-10 11:20
閱讀 1412·2019-08-30 13:47
閱讀 1309·2019-08-29 12:36
閱讀 3341·2019-08-26 10:43