1. 程式人生 > >Android執行緒池的使用及demo

Android執行緒池的使用及demo

一、為什麼要使用執行緒池:

   1、減少了建立執行緒的數量,提高了APP的效能

   2、節省開銷,防止併發執行緒過多,便於執行緒的管理

二、執行緒池的分類(四類)

  • newCachedThreadPool 快取型執行緒池: 如果池中沒有執行緒可用,它將建立 一個執行緒並新增到執行緒池中, 執行緒池中尚未使用的60秒執行緒 將會終止並從執行緒池中移出,因此, 對於長期保持足夠的空閒池不會消耗任何資源。

  • newFixedThreadPool 固定數目的執行緒池 執行緒池中的數量初始化後(n個執行緒), 執行緒數量n不變,啟用執行緒的最大數為n, 當需要的執行緒大於最大執行緒數量, 則其它執行緒在佇列中排隊等候,直到執行緒可用, 執行緒池的執行緒將一直存在,除非呼叫shutdown

  • newScheduledThreadPool 排程型執行緒池 建立一個執行緒池後,執行緒數量是固定, 並一直存線上程池中,它可指定執行緒延時、定時 週期性的執行

  • newSingleThreadExecutor 單執行緒池 建立執行緒池後,執行緒池有且只有一個執行緒,其它執行緒 在佇列中排隊等候。 與其他等效 newFixedThreadPool(1) 所返回的執行保證無需重新配置使用額外的執行緒。

三、執行緒池構造方法

ThreadPoolExecutor (int corePoolSize,
                int maximumPoolSize,
                long keepAliveTime,
                TimeUnit unit,
                BlockingQueue<Runnable> workQueue,
                ThreadFactory threadFactory)

corePoolSize 核心執行緒數量 maximumPoolSize 尚未執行的最大的執行緒數量 (即等候隊列當中的執行緒數) keepAliveTime 當執行緒數大於核心時, 這是多餘的空閒執行緒等待新任務終止前的最長時間。 unit 非核心執行緒數等待時間的單位 workQueue 佇列,用於儲存已提交的任務但 未執行的任務即等候的任務 threadFactory建立執行緒的工廠

四、執行緒池的使用

1、newCachedThreadPool

第一步:初始化執行緒池

    //建立執行緒池
     executorService = Executors.newCachedThreadPool(threadFactory);

原始碼分析 快取型執行緒池特性

   public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>(),
                                      threadFactory);
    }

從原始碼中我們發現快取型執行緒池核心執行緒數是0, 非核心執行緒數是整型的最大值, 非核心執行緒的空閒執行緒等待時間是60s 單位是秒,儲存非核心執行緒的佇列SynchronousQueue

第二步:建立任務(執行緒)

//任務
 private Runnable runnable1 = new Runnable() {
        @Override
        public void run() {
            try {
                Log.e("TAG","pb1:"+Thread.currentThread().getName());
                while(pb1.getProgress()< pb1.getMax()){
                    Thread.sleep(100);
                    pb1.setProgress(pb1.getProgress()+5);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    };

第三步: 提交任務

executorService.execute(runnable1);
2、newFixedThreadPool

第一步:初始化執行緒池

//初始化
 fixedExecutorService = Executors.newFixedThreadPool(corePoolSize,threadFactory);
原始碼分析 newFixedThreadPool特性
 public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>(),
                                      threadFactory);
    }

非核心執行緒數與核心執行緒數相等,非核心執行緒數的等待時間是0毫秒,單位是毫秒, 表示非核心空閒執行緒不存在等待時間,即不會從快取中移除

第二步:建立任務(執行緒)

//任務
 private Runnable runnable1 = new Runnable() {
        @Override
        public void run() {
            try {
                Log.e("TAG","pb1:"+Thread.currentThread().getName());
                while(pb1.getProgress()< pb1.getMax()){
                    Thread.sleep(100);
                    pb1.setProgress(pb1.getProgress()+5);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    };

第三步: 提交任務

fixedExecutorService.execute(runnable1);

3、newScheduledThreadPool

第一步:初始化執行緒池

//排程型執行緒池
 scheduledExecutorService = Executors.newScheduledThreadPool(corePoolSize,factory);

原始碼分析 排程型執行緒池特性

  public ScheduledThreadPoolExecutor(int corePoolSize,
                                       ThreadFactory threadFactory) {
        super(corePoolSize, Integer.MAX_VALUE,
              DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
              new DelayedWorkQueue(), threadFactory);
    }

corePoolSize核心執行緒數,非核心執行緒數為整型的最大值, 非核心執行緒的空閒執行緒等待時間10L,單位是毫秒, 已提交但未執行的任務儲存在DelayedWorkQueue佇列中, threadFactory是生產執行緒的執行緒池

第二步:建立任務(執行緒)

//任務
 private Runnable runnable1 = new Runnable() {
        @Override
        public void run() {
            try {
                Log.e("TAG","pb1:"+Thread.currentThread().getName());
                while(pb1.getProgress()< pb1.getMax()){
                    Thread.sleep(100);
                    pb1.setProgress(pb1.getProgress()+5);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    };

第三步 提交任務

提交方式1:

//非延時週期的的任務提交
scheduledExecutorService.execute(runnable1);

提交方式2:

//延時任務提交執行
scheduledExecutorService.schedule(runnable1,10L,TimeUnit.SECONDS);

10L表示延時的時間,TimeUnit.SECONDS表示延時的時間單位 即延時10秒後提交執行任務

提交方式3:

scheduledExecutorService.scheduleAtFixedRate
(Runnable command, long initialDelay, long period, TimeUnit unit)

command提交的任務,initialDelay延時的時間 period 任務啟動後每過period時間再次啟動任務 即週期性的執行任務 unit表示時間的單位

4、newSingleThreadExecutor

第一步:初始化執行緒池

//建立執行緒池
 singleExecutorService = Executors.newSingleThreadExecutor(factory);

原始碼分析 newSingleThreadExecutor執行緒池的特性

public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>(),
                                    threadFactory));
    }

核心執行緒數為1,非核心執行緒數為1, 非核心執行緒空閒執行緒等待時間0,即不從快取中移除, 非核心執行緒的的佇列採用LinkedBlockingQueue, threadFactory表示生產執行緒的工廠

第二步:建立任務(執行緒)

//任務
 private Runnable runnable1 = new Runnable() {
        @Override
        public void run() {
            try {
                Log.e("TAG","pb1:"+Thread.currentThread().getName());
                while(pb1.getProgress()< pb1.getMax()){
                    Thread.sleep(100);
                    pb1.setProgress(pb1.getProgress()+5);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    };

第三步 提交任務

singleExecutorService.execute(runnable1);

對比LinkedBlockingQueue和ArrayBlockingQueue區別結論:

(1)都是阻塞佇列. (2)儲存資料方式不一樣:ArrayBlockingQueue使用陣列方式,並且使用兩個下標來表明取出資料的位置和加入資料的位置. LinkedBlockingQueue使用的是單項鍊表方式儲存資料,使用頭和尾節點來指明連結串列取出資料和加入資料的地方,並且頭節點不儲存資料. 都通過變數來記錄儲存資料的數量:ArrayBlockingQueue使用int變數來記錄儲存資料數量,而LinkedBlockingQueue使用執行緒安全的AtomicInteger來記錄資料數量,很顯然AtomicInteger的效率更低. (3)由於ArrayBlockingQueue採用陣列方式儲存資料,所以其最大容易是在定義ArrayBlockingQueue的時候就已經確定的.不能再次修改. 而LinkedBlockingQueue採用連結串列儲存資料,所以其容易可以不用指定. (4)向對來說,由於ArrayBlockingQueue採用陣列來儲存資料,所有在加入資料和獲取資料時候效率都會更高. (5)都是使用ReentrantLock來實現執行緒安全,不過LinkedBlockingQueue採用了兩個重入鎖,並且使用了 AtomicInteger,所以相對來說實現同步ArrayBlockingQueue更簡單效率更高.

demo連結:  https://download.csdn.net/download/haoxuhong/10522474