天天看點

Android線程池封裝庫

目錄介紹

  • 1.遇到的問題和需求
  • 1.1 遇到的問題有哪些
  • 1.2 遇到的需求
  • 1.3 多線程通過實作Runnable弊端
  • 1.4 為什麼要用線程池
  • 2.封裝庫具有的功能
  • 2.1 常用的功能
  • 3.封裝庫的具體使用
  • 3.1 一鍵內建
  • 3.2 在application中初始化庫
  • 3.3 最簡單的runnable線程調用方式
  • 3.4 最簡單的異步回調
  • 4.線程池封裝思路介紹
  • 4.1 自定義Runnable和Callable類
  • 4.2 添加回調接口Callback
  • 4.3 建立線程池配置檔案
  • 4.4 建立java和android平台消息器
  • 4.5 建立PoolThread繼承Executor
  • 4.6 使用builder模式擷取線程池對象
  • 4.7 靈活建立線程池[重點]
  • 4.8 啟動線程池中的任務
  • 4.9 如何關閉線程池操作
  • 5.其他介紹
  • 5.1 參考的開源案例
  • 5.2 參考的部落格

好消息

  • 部落格筆記大彙總【16年3月到至今】,包括Java基礎及深入知識點,Android技術部落格,Python學習筆記等等,還包括平時開發中遇到的bug彙總,當然也在工作之餘收集了大量的面試題,長期更新維護并且修正,持續完善……開源的檔案是markdown格式的!同時也開源了生活部落格,從12年起,積累共計N篇[近100萬字,陸續搬到網上],轉載請注明出處,謝謝!
  • 連結位址: https://github.com/yangchong211/YCBlogs
  • 如果覺得好,可以star一下,謝謝!當然也歡迎提出建議,萬事起于忽微,量變引起質變!

0.前言介紹

  • 輕量級線程池封裝庫,支援線程執行過程中狀态回調監測(包含成功,失敗,異常等多種狀态);支援建立異步任務,并且可以設定線程的名稱,延遲執行時間,線程優先級,回調callback等;可以根據自己需要建立自己需要的線程池,一共有四種;線程異常時,可以列印異常日志,避免崩潰。
  • 關于線程池,對于開發來說是十分重要,但是又有點難以了解或者運用。關于寫線程池的部落格網上已經有很多了,但是一般很少有看到的實際案例或者封裝的庫,許多部落格也僅僅是介紹了線程池的概念,方法,或者部分源碼分析,那麼為了友善管理線程任務操作,是以才想結合實際案例是不是更容易了解線程池,更多可以參考代碼。

線程池封裝庫GitHub連結: https://github.com/yangchong211/YCThreadPool

1.1 遇到的問題有哪些?

  • 繼承Thread,或者實作接口Runnable來開啟一個子線程,無法準确地知道線程什麼時候執行完成并獲得到線程執行完成後傳回的結果
  • 當線程出現異常的時候,如何避免導緻崩潰問題? 部落格

  • 如何在實際開發中配置線程的優先級
  • 開啟一個線程,是否可以監聽Runnable接口中run方法操作的過程,比如監聽線程的狀态開始,成功,異常,完成等多種狀态。
  • 開啟一個線程,是否可以監聽Callable接口中call()方法操作的過程,比如監聽線程的狀态開始,錯誤異常,完成等多種狀态。

  • 1.3.1 一般開啟線程的操作如下所示
    new Thread(new Runnable() {
        @Override
        public void run() {
            //做一些任務
        }
    }).start();           
  • 建立了一個線程并執行,它在任務結束後GC會自動回收該線程。
  • 線上程并發不多的程式中确實不錯,而假如這個程式有很多地方需要開啟大量線程來處理任務,那麼如果還是用上述的方式去建立線程處理的話,那麼将導緻系統的性能表現的非常糟糕。
  • 1.3.2 主要的弊端有這些,可能總結并不全面
  • 大量的線程建立、執行和銷毀是非常耗cpu和記憶體的,這樣将直接影響系統的吞吐量,導緻性能急劇下降,如果記憶體資源占用的比較多,還很可能造成OOM
  • 大量的線程的建立和銷毀很容易導緻GC頻繁的執行,進而發生記憶體抖動現象,而發生了記憶體抖動,對于移動端來說,最大的影響就是造成界面卡頓
  • 線程的建立和銷毀都需要時間,當有大量的線程建立和銷毀時,那麼這些時間的消耗則比較明顯,将導緻性能上的缺失

  • 使用線程池管理線程優點
    • ①降低系統資源消耗,通過重用已存在的線程,降低線程建立和銷毀造成的消耗;
    • ②提高系統響應速度,當有任務到達時,無需等待新線程的建立便能立即執行;
    • ③友善線程并發數的管控,線程若是無限制的建立,不僅會額外消耗大量系統資源,更是占用過多資源而阻塞系統或oom等狀況,進而降低系統的穩定性。線程池能有效管控線程,統一配置設定、調優,提供資源使用率;
    • ④更強大的功能,線程池提供了定時、定期以及可控線程數等功能的線程池,使用友善簡單。

1.5 線程池執行流程

  • 大概的流程圖如下
  • 文字描述如下
    • ①如果線上程池中的線程數量沒有達到核心的線程數量,這時候就回啟動一個核心線程來執行任務。
    • ②如果線程池中的線程數量已經超過核心線程數,這時候任務就會被插入到任務隊列中排隊等待執行。
    • ③由于任務隊列已滿,無法将任務插入到任務隊列中。這個時候如果線程池中的線程數量沒有達到線程池所設定的最大值,那麼這時候就會立即啟動一個非核心線程來執行任務。
    • ④如果線程池中的數量達到了所規定的最大值,那麼就會拒絕執行此任務,這時候就會調用RejectedExecutionHandler中的rejectedExecution方法來通知調用者。

  • 支援線程執行過程中狀态回調監測(包含成功,失敗,異常等多種狀态)
  • 支援線程異常檢測,并且可以列印異常日志
  • 支援設定線程屬性,比如名稱,延時時長,優先級,callback
  • 支援異步開啟線程任務,支援監聽異步回調監聽
  • 友善內建,友善使用,可以靈活選擇建立不同的線程池

  • compile 'cn.yc:YCThreadPoolLib:1.3.0'

  • 代碼如下所示
    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() {
            // 建立一個獨立的執行個體進行使用
            executor = PoolThread.ThreadBuilder
                    .createFixed(5)
                    .setPriority(Thread.MAX_PRIORITY)
                    .setCallback(new LogCallback())
                    .build();
        }
    
        /**
         * 擷取線程池管理器對象,統一的管理器維護所有的線程池
         * @return                      executor對象
         */
        public PoolThread getExecutor(){
            return executor;
        }
    }
    
    
    //自定義回調監聽callback,可以全局設定,也可以單獨設定。都行
    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());
        }
    }           

  • 關于設定callback回調監聽,我這裡在app初始化的時候設定了全局的logCallBack,是以這裡沒有添加,對于每個單獨的執行任務,可以添加獨立callback。
    PoolThread executor = App.getInstance().getExecutor();
            executor.setName("最簡單的線程調用方式");
            executor.setDeliver(new AndroidDeliver());
            executor.execute(new Runnable() {
    @Override
    public void run() {
    Log.e("MainActivity","最簡單的線程調用方式");
    }
            });           

  • 如下所示
    PoolThread executor = App.getInstance().getExecutor();
            executor.setName("異步回調");
            executor.setDelay(2,TimeUnit.MILLISECONDS);
            // 啟動異步任務
            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.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調用了Runnable接口中的方法用來線上程中執行任務。Runnable 和 Callable 都代表那些要在不同的線程中執行的任務。
  • Thread調用了Runnable接口中的方法用來線上程中執行任務。
  • 4.1.3 Runnable和Callable接口有何差別
  • 它們的主要差別是 Callable 的 call() 方法可以傳回值和抛出異常,而 Runnable 的 run() 方法沒有這些功能。Callable 可以傳回裝載有計算結果的 Future 對象。
  • 比較兩個接口,可以得出這樣結論:
  • Callable 的任務執行後可傳回值,而 Runnable 的任務是不能傳回值的
  • call() 方法可以抛出異常,run()方法不可以的
  • 運作 Callable 任務可以拿到一個 Future 對象,表示異步計算的結果。它提供了檢查計算是否完成的方法,以等待計算的完成,并檢索計算的結果。通過 Future 對象可以了解任務執行情況,可取消任務的執行,還可擷取執行結果;
  • 4.1.4 自定義Runnable包裝類,重點看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);
        }
    
        /**
         * 啟動異步任務,普通的
         * @param runnable              runnable
         * @return                      對象
         */
        public RunnableWrapper setRunnable(Runnable runnable) {
            this.runnable = runnable;
            return this;
        }
    
        /**
         * 異步任務,回調用于接收可調用任務的結果
         * @param callable              callable
         * @return                      對象
         */
        public RunnableWrapper setCallable(Callable callable) {
            this.callable = callable;
            return this;
        }
    
        /**
         * 自定義xxRunnable繼承Runnable,實作run方法
         * 詳細可以看我的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();
                    //監聽成功
                    delegate.onSuccess(result);
                } catch (Exception e) {
                    //監聽異常
                    delegate.onError(name, e);
                }
            }
            //監聽完成
            delegate.onCompleted(name);
        }
    }           
  • 4.1.5 自定義Callable包裝類,重點看call方法代碼邏輯
    public final class CallableWrapper<T> implements Callable<T> {
    
        private String name;
        private ThreadCallback callback;
        private Callable<T> proxy;
    
        /**
         * 構造方法
         * @param configs               thread配置,主要參數有:線程name,延遲time,回調callback,異步callback
         * @param proxy                 線程優先級
         */
        public CallableWrapper(ThreadConfigs configs, Callable<T> proxy) {
            this.name = configs.name;
            this.proxy = proxy;
            this.callback = new CallbackDelegate(configs.callback, configs.deliver, configs.asyncCallback);
        }
    
        /**
         * 詳細可以看我的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();
                //異常錯誤
                if(callback!=null){
                    callback.onError(name,e);
                }
            }finally {
                //完成
                if (callback != null)  {
                    callback.onCompleted(name);
                }
            }
            return t;
        }
    }           

4. 添加回調接口AsyncCallback和ThreadCallback

  • 注意,這個寫的自定義callback,需要添加多種狀态,你可以自定義其他狀态。看完了這裡再回過頭看看RunnableWrapper中run方法和CallableWrapper中call方法的邏輯。
  • 4.0 為什麼要這樣設計
  • 它可以讓程式員準确地知道線程什麼時候執行完成并獲得到線程執行完成後傳回的結果。
  • AsyncCallback,在這個類中,可以看到三種狀态[這個是在自定義Runnable中的run方法中實作],并且成功時可以攜帶結果,在異常時還可以列印異常日志。
  • ThreadCallback,在這個類中,可以看到三種狀态[這個是在自定義Callable中的call方法中實作],并且在異常時可以列印異常日志,在開始和完成時可以列印線程名稱
  • 4.1 AsyncCallback類代碼如下所示
    /**
     * <pre>
     *     @author      楊充
     *     blog         https://www.jianshu.com/p/53017c3fc75d
     *     time
     *     desc         異步callback回調接口
     *     revise
     *     GitHub       https://github.com/yangchong211
     * </pre>
     */
    public interface AsyncCallback<T> {
    
        /**
         * 成功時調用
         * @param t         泛型
         */
        void onSuccess(T t);
    
        /**
         * 異常時調用
         * @param t         異常
         */
        void onFailed(Throwable t);
    
        /**
         * 通知使用者任務開始運作
         * @param threadName            正在運作線程的名字
         */
        void onStart(String threadName);
    }           
  • 4.2 ThreadCallback類代碼如下所示
    /**
     * <pre>
     *     @author: yangchong
     *     blog  : https://github.com/yangchong211
     *     time  :
     *     desc  : 一個回調接口,用于通知使用者任務的狀态回調委托類
     *             線程的名字可以自定義
     *     revise:
     * </pre>
     */
    public interface ThreadCallback {
    
        /**
         * 當線程發生錯誤時,将調用此方法。
         * @param threadName            正在運作線程的名字
         * @param t                     異常
         */
        void onError(String threadName, Throwable t);
    
        /**
         * 通知使用者知道它已經完成
         * @param threadName            正在運作線程的名字
         */
        void onCompleted(String threadName);
    
        /**
         * 通知使用者任務開始運作
         * @param threadName            正在運作線程的名字
         */
        void onStart(String threadName);
    }           

  • 為什麼要添加配置檔案,配置檔案的作用主要是存儲目前任務的某些配置,比如線程的名稱,回調callback等等這些參數。還可以用于參數的傳遞!
    public final class ThreadConfigs {
        /**
         * 線程的名稱
         * 通過setName方法設定
         */
        public String name;
        /**
         * 線程執行延遲的時間
         * 通過setDelay方法設定
         */
        public long delay;
        /**
         * 線程執行者
         * JAVA或者ANDROID
         */
        public Executor deliver;
        /**
         * 使用者任務的狀态回調callback
         */
        public ThreadCallback callback;
        /**
         * 異步callback回調callback
         */
        public AsyncCallback asyncCallback;
    }           

4.4 建立java平台和android平台消息器Executor

  • 在android環境下,想一想callback回調類中的幾個方法,比如回調失敗,回調成功,或者回調完成,可能會做一些操作UI界面的操作邏輯,那麼都知道子線程是不能更新UI的,是以必須放到主線程中操作。
  • 但是在Java環境下,回調方法所運作的線程與任務執行線程其實可以保持一緻。
  • 是以,這裡需要設定該消息器用來差別回調的邏輯。主要作用是指定回調任務需要運作在什麼線程之上。
  • 4.4.1 android環境下
  • 4.4.2 java環境下
  • 4.4.3 如何判斷環境是java環境還是Android環境呢
    public final class ThreadToolUtils {
    
        /**
         * 标志:是否在android平台上
         */
        public static boolean isAndroid;
        /*
         * 靜态代碼塊
         * 判斷是否是android環境
         * Class.forName(xxx.xx.xx) 傳回的是一個類對象
         * 首先要明白在java裡面任何class都要裝載在虛拟機上才能運作。
         */
        static {
            try {
                Class.forName("android.os.Build");
                isAndroid = true;
            } catch (Exception e) {
                isAndroid = false;
            }
        }
    }           

  • 這裡隻是展示部分代碼,如果想看完整的代碼,可以直接看案例。
  • 4.5.1 繼承Executor接口,并且實作execute方法
    public final class PoolThread implements Executor{
    
       
        /**
         * 啟動任務
         * 這個是實作接口Executor中的execute方法
         * 送出任務無傳回值
         * @param runnable              task,注意添加非空注解
         */
        @Override
        public void execute (@NonNull Runnable runnable) {
            //擷取線程thread配置資訊
            ThreadConfigs configs = getLocalConfigs();
            //設定runnable任務
            runnable = new RunnableWrapper(configs).setRunnable(runnable);
            //啟動任務
            DelayTaskDispatcher.get().postDelay(configs.delay, pool, runnable);
            //重置線程Thread配置
            resetLocalConfigs();
        }
    
        /**
         * 當啟動任務或者發射任務之後需要調用該方法
         * 重置本地配置,置null
         */
        private synchronized void resetLocalConfigs() {
            local.set(null);
        }
    
    
        /**
         * 注意需要用synchronized修飾,解決了多線程的安全問題
         * 擷取本地配置參數
         * @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.1 看下builder模式下代碼
  • 如果還不是很熟悉builder模式,歡迎閱讀我的另外一篇文章之——設計模式之二:Builder模式: https://www.jianshu.com/p/246b01ca84c2
  • 也可以看Android源碼設計模式這本書,很不錯
  • 直接列出代碼,如下所示:
    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()建立線程池
             * 内部隻有一個核心線程,所有任務進來都要排隊按順序執行
             */
            public static ThreadBuilder create(ExecutorService pool) {
                return new ThreadBuilder(1, TYPE_SINGLE, pool);
            }
    
            /**
             * 通過Executors.newCachedThreadPool()建立線程池
             * 它是一個數量無限多的線程池,都是非核心線程,适合執行大量耗時小的任務
             */
            public static ThreadBuilder createCacheable() {
                return new ThreadBuilder(0, TYPE_CACHE, null);
            }
    
            /**
             * 通過Executors.newFixedThreadPool()建立線程池
             * 線程數量固定的線程池,全部為核心線程,響應較快,不用擔心線程會被回收。
             */
            public static ThreadBuilder createFixed(int size) {
                return new ThreadBuilder(size, TYPE_FIXED, null);
            }
    
            /**
             * 通過Executors.newScheduledThreadPool()建立線程池
             * 有數量固定的核心線程,且有數量無限多的非核心線程,适合用于執行定時任務和固定周期的重複任務
             */
            public static ThreadBuilder createScheduled(int size) {
                return new ThreadBuilder(size, TYPE_SCHEDULED, null);
            }
    
            /**
             * 通過Executors.newSingleThreadPool()建立線程池
             * 内部隻有一個核心線程,所有任務進來都要排隊按順序執行
             * 和create差別是size數量
             */
            public static ThreadBuilder createSingle() {
                return new ThreadBuilder(0, TYPE_SINGLE, null);
            }
    
            /**
             * 将預設線程名設定為“已使用”。
             */
            public ThreadBuilder setName (@NonNull String name) {
                if (name.length()>0) {
                    this.name = name;
                }
                return this;
            }
    
            /**
             * 将預設線程優先級設定為“已使用”。
             */
            public ThreadBuilder setPriority (int priority) {
                this.priority = priority;
                return this;
            }
    
            /**
             * 将預設線程回調設定為“已使用”。
             */
            public ThreadBuilder setCallback (ThreadCallback callback) {
                this.callback = callback;
                return this;
            }
    
            /**
             * 設定預設線程傳遞使用
             */
            public ThreadBuilder setDeliver(Executor deliver) {
                this.deliver = deliver;
                return this;
            }
    
            /**
             * 建立用于某些配置的線程管理器。
             * @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) {
                    // 如果沒有設定名字,那麼就使用下面預設的線程名稱
                    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 添加設定thread配置資訊的方法
    /**
     * 為目前的任務設定線程名。
     * @param name              線程名字
     * @return                  PoolThread
     */
    public PoolThread setName(String name) {
        getLocalConfigs().name = name;
        return this;
    }
    
    
    /**
     * 設定目前任務的線程回調,如果未設定,則應使用預設回調。
     * @param callback          線程回調
     * @return                  PoolThread
     */
    public PoolThread setCallback (ThreadCallback callback) {
        getLocalConfigs().callback = callback;
        return this;
    }
    
    /**
     * 設定目前任務的延遲時間.
     * 隻有當您的線程池建立時,它才會産生效果。
     * @param time              時長
     * @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;
    }
    
    /**
     * 設定目前任務的線程傳遞。如果未設定,則應使用預設傳遞。
     * @param deliver           thread deliver
     * @return                  PoolThread
     */
    public PoolThread setDeliver(Executor deliver){
        getLocalConfigs().deliver = deliver;
        return this;
    }           
  • 4.6.3 看下builder模式下建立對象的代碼
  • 通過調用ThreadBuilder類中的build()方法建立屬于自己的線程池。
  • 最後通過new PoolThread(type, size, priority, name, callback, deliver, pool)建立對象,并且作為傳回值傳回。
  • 然後再來看看PoolThread方法,這部分看目錄4.7部分介紹。

  • 4.7.1 建立線程池的五種方法
  • 通過Executors的工廠方法擷取這五種線程池
  • 通過Executors的工廠方法來建立線程池極其簡便,其實它的内部還是通過new ThreadPoolExecutor(…)的方式建立線程池的,具體可以看看源碼,這裡省略呢……
    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 靈活建立不同類型線程池
  • 設計的時候,希望能夠選擇性地建立自己想要的線程池,并且動态設定線程的數量,還可以設定線程優先級。
  • 4.7.2.1 建立不同類型線程池代碼如下所示:
    /**
     * 建立線程池,目前支援以下四種
     * @param type                  類型
     * @param size                  數量size
     * @param priority              優先級
     * @return
     */
    private ExecutorService createPool(int type, int size, int priority) {
        switch (type) {
            case Builder.TYPE_CACHE:
                //它是一個數量無限多的線程池,都是非核心線程,适合執行大量耗時小的任務
                return Executors.newCachedThreadPool(new DefaultFactory(priority));
            case Builder.TYPE_FIXED:
                //線程數量固定的線程池,全部為核心線程,響應較快,不用擔心線程會被回收。
                return Executors.newFixedThreadPool(size, new DefaultFactory(priority));
            case Builder.TYPE_SCHEDULED:
                //有數量固定的核心線程,且有數量無限多的非核心線程,适合用于執行定時任務和固定周期的重複任務
                return Executors.newScheduledThreadPool(size, new DefaultFactory(priority));
            case Builder.TYPE_SINGLE:
            default:
                //内部隻有一個核心線程,所有任務進來都要排隊按順序執行
                return Executors.newSingleThreadExecutor(new DefaultFactory(priority));
        }
    }           
  • 4.7.2.1 了解一下ThreadFactory接口作用
  • 關于ThreadFactory接口的源代碼如下所示:
  • 以看到ThreadFactory中,隻有一個newThread方法,它負責接收一個Runnable對象,并将其封裝到Thread對象中,進行執行。
  • 通過有道詞典對這個類的說明進行翻譯是:根據需要建立新線程的對象。使用線程工廠可以消除對{@link Thread#Thread(Runnable)新線程}的硬連接配接,進而使應用程式能夠使用特殊的線程子類、優先級等。
    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 建立預設MyThreadFactory繼承ThreadFactory
  • 建立一個預設的MyThreadFactory,并且這個類繼承ThreadFactory,實作接口中的newThread方法。然後在newThread方法中建立線程,并且設定線程優先級。
  • 建立一個優先級線程池非常有用,它可以線上程池中線程數量不足或系統資源緊張時,優先處理我們想要先處理的任務,而優先級低的則放到後面再處理,這極大改善了系統預設線程池以FIFO方式處理任務的不靈活。
  • 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;
        }
    
    }           

  • 具體邏輯看DelayTaskExecutor中的postDelay方法
    /**
     * 啟動
     * @param delay                     延遲執行的時間,注意預設機關是TimeUnit.MILLISECONDS
     * @param pool                      pool線程池
     * @param task                      runnable
     */
    void postDelay(long delay, final ExecutorService pool, final Runnable task) {
        if (delay == 0) {
            //如果時間是0,那麼普通開啟
            pool.execute(task);
            return;
        }
    
        //延時操作
        dispatcher.schedule(new Runnable() {
            @Override
            public void run() {
                //在将來的某個時間執行給定的指令。該指令可以在新線程、池線程或調用線程中執行
                pool.execute(task);
            }
        }, delay, TimeUnit.MILLISECONDS);
    }           

  • /**
     * 關閉線程池操作
     */
    public void stop(){
        try {
            // shutdown隻是起到通知的作用
            // 隻調用shutdown方法結束線程池是不夠的
            pool.shutdown();
            // (所有的任務都結束的時候,傳回TRUE)
            if(!pool.awaitTermination(0, TimeUnit.MILLISECONDS)){
                // 逾時的時候向線程池中所有的線程發出中斷(interrupted)。
                pool.shutdownNow();
            }
        } catch (InterruptedException e) {
            // awaitTermination方法被中斷的時候也中止線程池中全部的線程的執行。
            e.printStackTrace();
        } finally {
            pool.shutdownNow();
        }
    }           

其他介紹

01.關于部落格彙總連結

02.關于我的部落格