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/yangchong2...
  • 若是以爲好,能夠star一下,謝謝!固然也歡迎提出建議,萬事起於忽微,量變引發質變!

0.前言介紹

  • 輕量級線程池封裝庫,支持線程執行過程當中狀態回調監測(包含成功,失敗,異常等多種狀態);支持建立異步任務,而且能夠設置線程的名稱,延遲執行時間,線程優先級,回調callback等;能夠根據本身須要建立本身須要的線程池,一共有四種;線程異常時,能夠打印異常日誌,避免崩潰。
  • 關於線程池,對於開發來講是十分重要,可是又有點難以理解或者運用。關於寫線程池的博客網上已經有不少了,可是通常不多有看到的實際案例或者封裝的庫,許多博客也僅僅是介紹了線程池的概念,方法,或者部分源碼分析,那麼爲了方便管理線程任務操做,因此纔想結合實際案例是否是更容易理解線程池,更多能夠參考代碼。

線程池封裝庫GitHub連接:https://github.com/yangchong2...

1.遇到的問題和需求

1.1 遇到的問題有哪些?

  • 繼承Thread,或者實現接口Runnable來開啓一個子線程,沒法準確地知道線程何時執行完成並得到到線程執行完成後返回的結果
  • 當線程出現異常的時候,如何避免致使崩潰問題?博客

1.2 遇到的需求

  • 如何在實際開發中配置線程的優先級
  • 開啓一個線程,是否能夠監聽Runnable接口中run方法操做的過程,好比監聽線程的狀態開始,成功,異常,完成等多種狀態。
  • 開啓一個線程,是否能夠監聽Callable<T>接口中call()方法操做的過程,好比監聽線程的狀態開始,錯誤異常,完成等多種狀態。

1.3 多線程經過實現Runnable弊端

  • 1.3.1 通常開啓線程的操做以下所示php

    new Thread(new Runnable() {
        @Override
        public void run() {
            //作一些任務
        }
    }).start();
  • 建立了一個線程並執行,它在任務結束後GC會自動回收該線程。
  • 在線程併發很少的程序中確實不錯,而假如這個程序有不少地方須要開啓大量線程來處理任務,那麼若是仍是用上述的方式去建立線程處理的話,那麼將致使系統的性能表現的很是糟糕。博客
  • 1.3.2 主要的弊端有這些,可能總結並不全面
  • 大量的線程建立、執行和銷燬是很是耗cpu和內存的,這樣將直接影響系統的吞吐量,致使性能急劇降低,若是內存資源佔用的比較多,還極可能形成OOM
  • 大量的線程的建立和銷燬很容易致使GC頻繁的執行,從而發生內存抖動現象,而發生了內存抖動,對於移動端來講,最大的影響就是形成界面卡頓
  • 線程的建立和銷燬都須要時間,當有大量的線程建立和銷燬時,那麼這些時間的消耗則比較明顯,將致使性能上的缺失

1.4 爲何要用線程池

  • 使用線程池管理線程優勢html

    • ①下降系統資源消耗,經過重用已存在的線程,下降線程建立和銷燬形成的消耗;
    • ②提升系統響應速度,當有任務到達時,無需等待新線程的建立便能當即執行;
    • ③方便線程併發數的管控,線程如果無限制的建立,不只會額外消耗大量系統資源,更是佔用過多資源而阻塞系統或oom等情況,從而下降系統的穩定性。線程池能有效管控線程,統一分配、調優,提供資源使用率;
    • ④更強大的功能,線程池提供了定時、按期以及可控線程數等功能的線程池,使用方便簡單。

1.5 線程池執行流程

  • 大概的流程圖以下java

    • image
  • 文字描述以下android

    • ①若是在線程池中的線程數量沒有達到核心的線程數量,這時候就回啓動一個核心線程來執行任務。
    • ②若是線程池中的線程數量已經超過核心線程數,這時候任務就會被插入到任務隊列中排隊等待執行。
    • ③因爲任務隊列已滿,沒法將任務插入到任務隊列中。這個時候若是線程池中的線程數量沒有達到線程池所設定的最大值,那麼這時候就會當即啓動一個非核心線程來執行任務。
    • ④若是線程池中的數量達到了所規定的最大值,那麼就會拒絕執行此任務,這時候就會調用RejectedExecutionHandler中的rejectedExecution方法來通知調用者。博客

2.封裝庫具備的功能

2.1 經常使用的功能

  • 支持線程執行過程當中狀態回調監測(包含成功,失敗,異常等多種狀態)
  • 支持線程異常檢測,而且能夠打印異常日誌
  • 支持設置線程屬性,好比名稱,延時時長,優先級,callback
  • 支持異步開啓線程任務,支持監聽異步回調監聽
  • 方便集成,方便使用,能夠靈活選擇建立不一樣的線程池

3.封裝庫的具體使用

3.1 一鍵集成

  • compile 'cn.yc:YCThreadPoolLib:1.3.0'

3.2 在application中初始化庫

  • 代碼以下所示git

    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());
    }
}
```

3.3 最簡單的runnable線程調用方式

  • 關於設置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","最簡單的線程調用方式");
                }
            });

3.4 最簡單的異步回調

  • 以下所示github

    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.線程池封裝思路介紹

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方法代碼邏輯segmentfault

    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
*/
    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<T>包裝類,重點看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
*/
    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<T>中的call方法中實現],而且在異常時能夠打印異常日誌,在開始和完成時能夠打印線程名稱
  • 4.1 AsyncCallback類代碼以下所示

    /**
     * <pre>
     *     @author      楊充
     *     blog         https://www.jianshu.com/p/53017c3fc75d
     *     time
     *     desc         異步callback回調接口
     *     revise
     *     GitHub       https://github.com/yangchong211
*/
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:
*/
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);
}
```

4.3 建立線程池配置文件

  • 爲何要添加配置文件,配置文件的做用主要是存儲當前任務的某些配置,好比線程的名稱,回調callback等等這些參數。還能夠用於參數的傳遞!

    public final class ThreadConfigs {
        /**
         * 線程的名稱
*/
    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環境下

image

  • 4.4.2 java環境下

image

  • 4.4.3 如何判斷環境是java環境仍是Android環境呢

    public final class ThreadToolUtils {
    
        /**
*/
    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 建立PoolThread繼承Executor

  • 這裏只是展現部分代碼,若是想看完整的代碼,能夠直接看案例。博客
  • 4.5.1 繼承Executor接口,而且實現execute方法

    public final class PoolThread implements Executor{
    
       
        /**
         * 啓動任務
         * 這個是實現接口Executor中的execute方法
         * 提交任務無返回值
*/
    @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 使用builder模式獲取線程池對象

  • 4.6.1 看下builder模式下代碼
  • 若是還不是很熟悉builder模式,歡迎閱讀個人另一篇文章之——設計模式之二:Builder模式:https://www.jianshu.com/p/246...
  • 也能夠看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              線程名字
*/
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 靈活建立線程池[重點]

  • 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              優先級
*/
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
*
     * @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;
        }
    
    }

4.8 啓動線程池中的任務

  • 具體邏輯看DelayTaskExecutor中的postDelay方法

    /**
     * 啓動
     * @param delay                     延遲執行的時間,注意默認單位是TimeUnit.MILLISECONDS
     * @param pool                      pool線程池
*/
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);
}
```

4.9 如何關閉線程池操做

  • 代碼以下所示

    /**
*/
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();
    }
}
```

5.其餘介紹

5.1 參考的開源案例

5.2 參考的博客

其餘介紹

01.關於博客彙總連接

02.關於個人博客

線程池封裝庫GitHub連接:https://github.com/yangchong2...

相關文章
相關標籤/搜索