1. 程式人生 > >Java執行緒池(ExecutorService)使用

Java執行緒池(ExecutorService)使用

一、前提

/**
 * 執行緒執行demo,執行時打出執行緒id以及傳入執行緒中引數
 */
public class ThreadRunner implements Runnable {

    private final SimpleDateFormat format = new SimpleDateFormat("HH:mm:ss.SSS");

    /**
     * 執行緒私有屬性,建立執行緒時建立
     */
    private Integer num;

    public ThreadRunner(Integer num) {
        this.num = num;
    }

    @Override
public void run() { System.out.println("thread:" + Thread.currentThread().getName() + ",time:" + format.format(new Date()) + ",num:" + num); try {//使執行緒睡眠,模擬執行緒阻塞情況 TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } } }

二、分類

1、FixedThreadPool-有一個固定大小的執行緒池

public class FixedThreadPoolDemo {

    public static void main(String[] args) {
        ExecutorService pool = Executors.newFixedThreadPool(4);
        for(int i = 0 ; i < 50 ; i++){
            pool.submit(new ThreadRunner((i + 1)));
        }
        pool.shutdown();
    }
}

輸出:

thread:pool-1-thread-2,time:16:14:45.677,num:2
thread:pool-1-thread-4,time:16:14:45.678,num:4
thread:pool-1-thread-3,time:16:14:45.680,num:3
thread:pool-1-thread-1,time:16:14:45.684,num:1
thread:pool-1-thread-4,time:16:14:46.680,num:5
thread:pool-1-thread-2,time:16:14:46.680,num:6
thread:pool-1-thread-3,time:16:14:46.680,num:7
thread:pool-1-thread-1,time:16:14:46.684,num:8
thread:pool-1-thread-4,time:16:14:47.680,num:9
thread:pool-1-thread-2,time:16:14:47.680,num:10
thread:pool-1-thread-3,time:16:14:47.681,num:11
thread:pool-1-thread-1,time:16:14:47.684,num:12
thread:pool-1-thread-4,time:16:14:48.681,num:13
thread:pool-1-thread-2,time:16:14:48.681,num:14
thread:pool-1-thread-3,time:16:14:48.681,num:15
thread:pool-1-thread-1,time:16:14:48.684,num:16
thread:pool-1-thread-4,time:16:14:49.681,num:17
thread:pool-1-thread-2,time:16:14:49.682,num:18
thread:pool-1-thread-3,time:16:14:49.682,num:19
thread:pool-1-thread-1,time:16:14:49.684,num:20
thread:pool-1-thread-4,time:16:14:50.681,num:21
thread:pool-1-thread-2,time:16:14:50.682,num:22
thread:pool-1-thread-3,time:16:14:50.682,num:23
thread:pool-1-thread-1,time:16:14:50.684,num:24
thread:pool-1-thread-4,time:16:14:51.681,num:25
thread:pool-1-thread-2,time:16:14:51.682,num:26
thread:pool-1-thread-3,time:16:14:51.682,num:27
thread:pool-1-thread-1,time:16:14:51.684,num:28
thread:pool-1-thread-4,time:16:14:52.681,num:29
thread:pool-1-thread-2,time:16:14:52.682,num:30
thread:pool-1-thread-3,time:16:14:52.682,num:31
thread:pool-1-thread-1,time:16:14:52.684,num:32
thread:pool-1-thread-4,time:16:14:53.681,num:33
thread:pool-1-thread-2,time:16:14:53.682,num:34
thread:pool-1-thread-3,time:16:14:53.683,num:35
thread:pool-1-thread-1,time:16:14:53.685,num:36
thread:pool-1-thread-2,time:16:14:54.682,num:38
thread:pool-1-thread-4,time:16:14:54.682,num:37
thread:pool-1-thread-3,time:16:14:54.683,num:39
thread:pool-1-thread-1,time:16:14:54.686,num:40
thread:pool-1-thread-2,time:16:14:55.682,num:41
thread:pool-1-thread-4,time:16:14:55.682,num:42
thread:pool-1-thread-3,time:16:14:55.683,num:43
thread:pool-1-thread-1,time:16:14:55.686,num:44
thread:pool-1-thread-2,time:16:14:56.682,num:45
thread:pool-1-thread-4,time:16:14:56.683,num:46
thread:pool-1-thread-3,time:16:14:56.684,num:47
thread:pool-1-thread-1,time:16:14:56.686,num:48
thread:pool-1-thread-2,time:16:14:57.683,num:49
thread:pool-1-thread-4,time:16:14:57.683,num:50

總結:
- 池中執行緒數量固定,不會發生變化
- 使用無界的LinkedBlockingQueue,要綜合考慮生成與消費能力,生成過剩,可能導致堆記憶體溢位。
- 適用一些很穩定很固定的正規併發執行緒,多用於伺服器

2、CachedThreadPool

public class CachedThreadPoolDemo {

    public static void main(String[] args) {
        ExecutorService pool = Executors.newCachedThreadPool();
        for(int i = 0 ; i < 50 ; i++){
            pool.submit(new ThreadRunner((i + 1)));
        }
        pool.shutdown();
    }
}

輸出:

thread:pool-1-thread-2,time:16:17:21.289,num:2
thread:pool-1-thread-3,time:16:17:21.290,num:3
thread:pool-1-thread-4,time:16:17:21.290,num:4
thread:pool-1-thread-6,time:16:17:21.291,num:6
thread:pool-1-thread-7,time:16:17:21.291,num:7
thread:pool-1-thread-8,time:16:17:21.291,num:8
thread:pool-1-thread-1,time:16:17:21.292,num:1
thread:pool-1-thread-10,time:16:17:21.293,num:10
thread:pool-1-thread-5,time:16:17:21.294,num:5
thread:pool-1-thread-11,time:16:17:21.294,num:11
thread:pool-1-thread-15,time:16:17:21.294,num:15
thread:pool-1-thread-9,time:16:17:21.294,num:9
thread:pool-1-thread-16,time:16:17:21.295,num:16
thread:pool-1-thread-20,time:16:17:21.295,num:20
thread:pool-1-thread-14,time:16:17:21.296,num:14
thread:pool-1-thread-12,time:16:17:21.296,num:12
thread:pool-1-thread-19,time:16:17:21.297,num:19
thread:pool-1-thread-13,time:16:17:21.299,num:13
thread:pool-1-thread-17,time:16:17:21.300,num:17
thread:pool-1-thread-18,time:16:17:21.302,num:18
thread:pool-1-thread-22,time:16:17:21.304,num:22
thread:pool-1-thread-23,time:16:17:21.304,num:23
thread:pool-1-thread-24,time:16:17:21.305,num:24
thread:pool-1-thread-21,time:16:17:21.305,num:21
thread:pool-1-thread-26,time:16:17:21.305,num:26
thread:pool-1-thread-25,time:16:17:21.306,num:25
thread:pool-1-thread-29,time:16:17:21.307,num:29
thread:pool-1-thread-28,time:16:17:21.308,num:28
thread:pool-1-thread-30,time:16:17:21.308,num:30
thread:pool-1-thread-34,time:16:17:21.308,num:34
thread:pool-1-thread-35,time:16:17:21.308,num:35
thread:pool-1-thread-33,time:16:17:21.308,num:33
thread:pool-1-thread-27,time:16:17:21.309,num:27
thread:pool-1-thread-32,time:16:17:21.308,num:32
thread:pool-1-thread-31,time:16:17:21.309,num:31
thread:pool-1-thread-36,time:16:17:21.310,num:36
thread:pool-1-thread-37,time:16:17:21.310,num:37
thread:pool-1-thread-38,time:16:17:21.310,num:38
thread:pool-1-thread-42,time:16:17:21.310,num:42
thread:pool-1-thread-40,time:16:17:21.310,num:40
thread:pool-1-thread-41,time:16:17:21.311,num:41
thread:pool-1-thread-47,time:16:17:21.762,num:47
thread:pool-1-thread-43,time:16:17:21.762,num:43
thread:pool-1-thread-39,time:16:17:21.762,num:39
thread:pool-1-thread-45,time:16:17:21.762,num:45
thread:pool-1-thread-44,time:16:17:21.763,num:44
thread:pool-1-thread-46,time:16:17:21.761,num:46
thread:pool-1-thread-48,time:16:17:21.761,num:48
thread:pool-1-thread-49,time:16:17:21.765,num:49
thread:pool-1-thread-50,time:16:17:21.765,num:50

總結
- 池中執行緒時隨著處理資料增加而增加
- 執行緒數並不是一直增加,如果有新任務需要執行時,首先查詢池中是否有空閒執行緒並且還為到空閒截止時間,如果有,則使用空閒執行緒,如果沒有,則建立新執行緒並放入池中。
- 用於執行一些生存期很短的非同步型任務。不適用於IO等長延時操作,因為這可能會建立大量執行緒,導致系統崩潰。
- 使用SynchronousQueue作為阻塞佇列,如果有新任務進入佇列,必須佇列中資料被其他執行緒處理,否則會等待。

3、SingleThreadExecutor

public class SingleThreadPoolDemo {

    public static void main(String[] args) {
        ExecutorService pool = Executors.newSingleThreadExecutor();
        for(int i = 0 ; i < 50 ; i++){
            pool.submit(new ThreadRunner((i + 1)));
        }
        pool.shutdown();
    }
}

輸出:

thread:pool-1-thread-1,time:16:20:10.194,num:1
thread:pool-1-thread-1,time:16:20:11.197,num:2
thread:pool-1-thread-1,time:16:20:12.197,num:3
thread:pool-1-thread-1,time:16:20:13.197,num:4
thread:pool-1-thread-1,time:16:20:14.197,num:5
thread:pool-1-thread-1,time:16:20:15.198,num:6
thread:pool-1-thread-1,time:16:20:16.198,num:7
thread:pool-1-thread-1,time:16:20:17.198,num:8
thread:pool-1-thread-1,time:16:20:18.198,num:9
thread:pool-1-thread-1,time:16:20:19.198,num:10
thread:pool-1-thread-1,time:16:20:20.198,num:11
thread:pool-1-thread-1,time:16:20:21.199,num:12
thread:pool-1-thread-1,time:16:20:22.200,num:13
thread:pool-1-thread-1,time:16:20:23.200,num:14
thread:pool-1-thread-1,time:16:20:24.200,num:15
thread:pool-1-thread-1,time:16:20:25.200,num:16
thread:pool-1-thread-1,time:16:20:26.201,num:17
thread:pool-1-thread-1,time:16:20:27.201,num:18
thread:pool-1-thread-1,time:16:20:28.201,num:19
thread:pool-1-thread-1,time:16:20:29.201,num:20
thread:pool-1-thread-1,time:16:20:30.202,num:21
thread:pool-1-thread-1,time:16:20:31.202,num:22
thread:pool-1-thread-1,time:16:20:32.203,num:23
thread:pool-1-thread-1,time:16:20:33.203,num:24
thread:pool-1-thread-1,time:16:20:34.203,num:25
thread:pool-1-thread-1,time:16:20:35.203,num:26
thread:pool-1-thread-1,time:16:20:36.203,num:27
thread:pool-1-thread-1,time:16:20:37.203,num:28
thread:pool-1-thread-1,time:16:20:38.203,num:29
thread:pool-1-thread-1,time:16:20:39.203,num:30
thread:pool-1-thread-1,time:16:20:40.203,num:31
thread:pool-1-thread-1,time:16:20:41.203,num:32
thread:pool-1-thread-1,time:16:20:42.203,num:33
thread:pool-1-thread-1,time:16:20:43.204,num:34
thread:pool-1-thread-1,time:16:20:44.204,num:35
thread:pool-1-thread-1,time:16:20:45.204,num:36
thread:pool-1-thread-1,time:16:20:46.204,num:37
thread:pool-1-thread-1,time:16:20:47.205,num:38
thread:pool-1-thread-1,time:16:20:48.205,num:39
thread:pool-1-thread-1,time:16:20:49.205,num:40
thread:pool-1-thread-1,time:16:20:50.206,num:41
thread:pool-1-thread-1,time:16:20:51.206,num:42
thread:pool-1-thread-1,time:16:20:52.207,num:43
thread:pool-1-thread-1,time:16:20:53.207,num:44
thread:pool-1-thread-1,time:16:20:54.207,num:45
thread:pool-1-thread-1,time:16:20:55.207,num:46
thread:pool-1-thread-1,time:16:20:56.207,num:47
thread:pool-1-thread-1,time:16:20:57.208,num:48
thread:pool-1-thread-1,time:16:20:58.208,num:49
thread:pool-1-thread-1,time:16:20:59.209,num:50

總結:
- 執行緒中只有一個執行緒在執行
- 適用於有明確執行順序但是不影響主執行緒的任務,壓入池中的任務會按照佇列順序執行。
- 使用無界的LinkedBlockingQueue,要綜合考慮生成與消費能力,生成過剩,可能導致堆記憶體溢位。

三、原始碼

public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
 public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }

* ThreadPoolExecutor 構造方法*

public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue) {
    this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
         Executors.defaultThreadFactory(), defaultHandler);
}


 public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory,
                          RejectedExecutionHandler handler) {
    if (corePoolSize < 0 ||
        maximumPoolSize <= 0 ||
        maximumPoolSize < corePoolSize ||
        keepAliveTime < 0)
        throw new IllegalArgumentException();
    if (workQueue == null || threadFactory == null || handler == null)
        throw new NullPointerException();
    this.corePoolSize = corePoolSize;
    this.maximumPoolSize = maximumPoolSize;
    this.workQueue = workQueue;
    this.keepAliveTime = unit.toNanos(keepAliveTime);
    this.threadFactory = threadFactory;
    this.handler = handler;
}
  • corePoolSize:執行緒池核心執行緒數量
    1. 如果池中執行緒數量少於核心執行緒池數量,則直接新建執行緒處理當前任務。
    2. 核心執行緒池空閒不會被回收。
    3. 當池中無空閒執行緒時,新任務將被新增到阻塞佇列
  • maximumPoolSize:執行緒池最大執行緒數量
    1. 當阻塞佇列已滿,並且有新任務還在入隊時,建立新的執行緒處理,直到執行緒數大於maximumPoolSize。
    2. 超出corePoolSize部分的執行緒超過空閒時間後會被回收
    3. 當執行緒已經超出corePoolSize,並且佇列容量已滿,則拒絕入隊。
  • keepAliveTime unit:執行緒存活時間
    1. 當執行緒超出corePoolSize時生效
    2. 執行緒空餘keepAliveTime後,將被回收
  • workQueue:執行緒使用阻塞佇列
  • threadFactory:建立執行緒池工廠
    1. 用於控制建立執行緒或者銷燬執行緒時加入其它邏輯
  • handler:執行緒池拒絕策略
    1. 直接丟棄(DiscardPolicy)
    2. 丟棄佇列中最老的任務(DiscardOldestPolicy)。
    3. 拋異常(AbortPolicy)
    4. 將任務分給呼叫執行緒來執行(CallerRunsPolicy)