Android線程池封裝庫(kù)

目錄介紹

  • 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 參考的開源案例

5.2 參考的博客

其他介紹

01.關(guān)于博客匯總鏈接

02.關(guān)于我的博客

線程池封裝庫(kù)GitHub鏈接:https://github.com/yangchong211/YCThreadPool

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容