線程池 Executor 一篇簡單筆記就懂

線程池Executor

目錄

1、線程池簡介

2、線程池的好處

3、線程池執行任務的規則

4、線程池的分類

1)FixedThreadPool類型

2)CachedThreadPool類型

3)ScheduleThreadPool類型

4)SingleThreadExecutor類型


1、線程池簡介

線程池是在java開發中很重要的一個概念,Android中的線程池和和java是保持一致的,並無什麼區別。線程池是一個抽象的概念,在java中是一個接口類,用Executor表示, 具體實現類爲ThreadPoolExecutor,它們位於java.util.concurrent包下面,這個接口類很短,而且接口聲明就只有一個,並且註釋中說明了一些情況怎麼使用,

package java.util.concurrent;
public interface Executor {

    /**
     * Executes the given command at some time in the future.  The command
     * may execute in a new thread, in a pooled thread, or in the calling
     * thread, at the discretion of the {@code Executor} implementation.
     *
     * @param command the runnable task
     * @throws RejectedExecutionException if this task cannot be
     * accepted for execution
     * @throws NullPointerException if command is null
     */
    void execute(Runnable command);
}

上面省略了註釋,可以看註釋,很管用,上面有模版,教我們怎麼創建一個自己線程池,其中有段是這麼說的:許多線程池的實現類對將要執行的任務的方式和時間做了限制,下面展示了串型線程池在執行任務的時候,從一個任務到下一個任務的過程。

下面代碼來至註釋中的舉例,說明了一個從任務如何進入到下一個任務的串行執行過程:

package demo.xx.patten.xx;

import java.util.ArrayDeque;
import java.util.Queue;
import java.util.concurrent.Executor;

public class SerialExecutor implements Executor {
    final Queue<Runnable> tasks = new ArrayDeque<>();
    final Executor executor;
    Runnable active;

    public SerialExecutor(Executor executor) {
        this.executor = executor;
    }

    public synchronized void execute(final Runnable r) {
        tasks.add(new Runnable() {
            public void run() {
                try {
                    r.run();
                } finally {
                    scheduleNext();
                }
            }
        });
        if (active == null) {
            scheduleNext();
        }
    }

    protected synchronized void scheduleNext() {
        if ((active = tasks.poll()) != null) {
            executor.execute(active);
        }
    }
    
}

上面的SerialExecutor管理了一個Runnable對象的任務列隊tasks和一個從構造函數中傳入的線程池,每次調用SerialExecutor的execute方法的時候都會傳入一個Runnable對象,然後將這個runnbale的執行方法run方法包裝進一個新建的Runnable對象中,然後將這個新建的Runnbale對象插入tasks隊列中。緊接着由於初始化的時候active是null,於是會首先執行scheduleNext()方法,這個時候通過tasks.poll()方法將插入的任務,也就是Runnable對象取出來交給構造函數中傳入的Executor對象實例來執行,注意上面try的finally塊中在r.run()被執行後都會被調用,這就保證了從一個任務到下一個任務的傳遞,因爲active在第一執行scheduleNext後會被負值,之後就不等於null了,那麼判斷語句就不會被執行了。

 

    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory) {
        
    }
  • corePollSize: 線程池的核心線程數的最大數量,默認情況下,核心線程會在在線程中一直存活,即使處於閒置狀態。但如果將ThreadPoolExecutor的allowCoreThreadTimeOut屬性設置爲true,則核心線程數在閒置一段時間後,會被回收,這個事件由keepAliveTime指定;
  • maxinumPoolSize: 線程池鎖容納的最大線程數,當活動線程達到這個數後,新來的任務將被阻塞;
  • keepAliveTime 非核心線程閒置的最大時長,超過後就會被回收,當allowCoreThreadTimeOut屬性設置爲true後,閒置的核心線程超過這個事件也會被回收;
  • unit 用於指定keepAliveTime參數的事件單位,這是一個枚舉類型,常用的有TimeUnit.MILLISECONDS, TimeUnit.SECONDS, TimeUnit.MINUTES等;
  • workQueue 線程池的任務隊列,通過線程池的execute方法提交的Runnable對象會存儲在這個隊列中;
  • threadFactory 線程工廠, 爲線程池提供創建新線程的功能,ThreadFactory只是一個接口,也只有一個方法: Thread newThread(Runnable r);

 

2、線程池的好處

  1. 線程複用,避免現線程創建和銷燬帶來的的性能開銷;
  2. 有效控制線程池的最大併發數,避免大量線程之間搶奪資源造成阻塞;
  3. 能夠對線程進行簡單的管理,並提供定時執行及指定間隔循環等功能;

 

3、線程池執行任務的規則

  1. 如果線程池中線程數量未達到核心線程的數量,會直接啓動一個核心線程去執行任務;
  2. 如果線程池中線程的數量已經達到或者超過了核心線程的數量,那麼接下來的任務會被插入到任務列隊(workQueue)中排隊等待執行;
  3. 如果workQueue中線程數量達到了列隊的上線,導致任務無法再插入任務列隊,並且線程池中的線程的數量沒有達到線程池設定的線程數上限,這種情況下會啓動非核心線程來執行任務;
  4. 如果步驟3中線程數量達到線程池規定最大值(maxmumPoolSize),那麼線程池會拒絕執行新添加的任務,並且ThreadPoolExecutor會調用RejectedExecutionHandler的的rejectedExecution方法來通知調用者。

 

4、線程池的分類

從前面的介紹我們瞭解到,要直接創建一個線程池,需要傳入很多個參數,這在實際使用中,就會顯得比較麻煩,所以在Executors這個線程池工具類中,給我們提供了更加簡單的創建線程池的API, 主要有四種類型,注意僅僅是類型,並不是存在的java類

 

1FixedThreadPool類型

通過Executors.newFixedThreadPool(int nThreads)創建

 創建一個固定線程數量的線程池,線程池中的所有線程都是核心線程,當線程空閒時不會被回收,也不會超時,除非線程池被關閉,當線程池中線程都處於活動狀態的時候,新進任務會處於等待狀態,直到有線程空出來去處理,處於等待狀態的任務會在任務列隊中排隊等候,這個列隊的容量沒有大小限制,由於線程不會被回收,這樣便能夠快速的響應外界請求。

 

2CachedThreadPool類型

通過Executors.newCachedThreadPool()創建

這是一個線程池的線程的數量是不定的,所有線程都是非核心線程,最大線程數量爲Integer.MAX_VALUE,當線程池中線程都處於活動狀態的時候,線程池會創建新的線程池來處理新進任務,否則就會利用空閒線程來處理新進任務,線程池中的線程都有60s的超時機制,超過時間的線程會被回收。

使用場景:大量的耗時較少的任務

 

3ScheduleThreadPool類型

通過Executors.newScheduleThreadPool(int corePoolSize)創建。

這種線程池的核心線程數是固定的,非核心線程數量沒有限制,並且非核心線程執行完任務會被立刻回收,非核心線程沒有閒置的狀態。

使用場景:定時任務和固定週期的重複任務

 

4SingleThreadExecutor類型

通過Executors.newSingleThreadExecutor()創建

SingleThreadExecutor內部只有唯一一個核心線程用來執行任務,其確保了所有的任務都在一個線程中執行,SingleThreadExecutor存在的意義在於統一所有的外界任務到一個線程中去執行,這樣這些任務就不需要處理線程同步的問題。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章