JAVA線程池 之 Executors (一) 簡介

1、背景java

  線程屬於系統稀缺資源,在使用線程時,若是無限制建立線程,達到CPU高負荷時,會致使系統運行緩慢,更有甚者直接宕機。數組

在這樣的基礎上咱們但願在使用線程時,竟可能使系統線程數處於一個可控範圍,儘量實現線程的重用。併發

 

2、Executors 分析app

Executors 示例  DEMOide

  

/**
 * @author binH
 * @date 2018/01/24
 */
package org.lsnbin.thread;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ThreadAnalyze {
    
    private    static ExecutorService fexecutor = Executors.newFixedThreadPool(20);
    
    public static void main(String[] args) {
        for (int i = 0; i < 20; i++) {
            fexecutor.execute(new ThreadAnalyze().new Task());
        }
    }
    
    private class Task implements Runnable{

        @Override
        public void run() {
            System.out.println("Thread name --> " + Thread.currentThread().getName());
        }
    }
}

  示例分析:高併發

  一、使用Executors初始化一個包含10個線程的線程池性能

  二、使用execute方法提交20個任務,打印線程名this

  三、負責執行任務的線程的生命週期交由Executors管理。spa

 

3、Executors 內部分析線程

  Executors是線程池的工廠類,內部基於ThreadPoolExecutor實現,對ThreadPoolExecutor進行封裝,並提供相對應方法。

  newFixedThreadPool

public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }

初始化必定數量
的線程池,當corePoolSize=maximumPoolSize時,使用LinkedBlockingQueue做爲阻塞隊列,不過當線程池不使用時,也不會釋放線程。

newCachedThreadPool

public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }
不初始化線程,在須要使用時建立線程,最大容許數爲
Integer.MAX_VALUE,使用SynchronousQueue做爲阻塞隊列,當線程空閒時keepAliveTime後釋放線程。
容易存在問題:在高併發狀況下,一瞬間會建立大量的線程,會出現嚴重的性能問題。

  newSingleThreadExecutor

 public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }
初始化只有一個線程的線程池,若是該線程因爲異常結束,會從新建立一個新的線程。線程不過時,使用
LinkedBlockingQueue做爲阻塞隊列。惟一個線程能夠保證任務的順序執行。

  newScheduledThreadPool

 public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
        return new ScheduledThreadPoolExecutor(corePoolSize);
    }
初始化必定數量線程的線程池,線程池中的任務能夠在指定的時間內週期性的執行所提交的任務。可用於按期同步。

  newWorkStealingPool  -- JDK1.8

 public static ExecutorService newWorkStealingPool(int parallelism) {
        return new ForkJoinPool
            (parallelism,
             ForkJoinPool.defaultForkJoinWorkerThreadFactory,
             null, true);
    }
建立持有足夠線程的線程池來支持給定的並行級別,並經過使用多個隊列來減小競爭,須要指定
parallelism並行參數,若是沒指定,則默認CPU數量。

  ForkJoinPool:支持大任務分解成小任務的線程池,這是Java8新增線程池,一般配合ForkJoinTask接口的子類RecursiveAction或RecursiveTask使用。

 

4、ThreadPoolExecutor 分析

 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.acc = System.getSecurityManager() == null ?
                null :
                AccessController.getContext();
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }

  參數分析:

  一、corePoolSize

    線程池核心線程數。當線程池提交一個任務時,線程池就會建立一個新線程並執行任務,直到線程池內運行狀態的線程數等於corePoolSize。

    當繼續有任務提交時,會被放入阻塞隊列中。使用 prestartCoreThread() 方法能夠提早預建立並啓動全部核心線程。

  二、maximumPoolSize

    線程池最大可用線程數。在線程數等於corePoolSize時,若是當前阻塞隊列滿了,繼續提交任務時會建立新的線程並執行任務。前提時線程數小於maximumPoolSize。

  三、keepAliveTime

    線程池中線程空閒存活時間。默認狀況下該狀況只會在大於corePoolSize時有用。可是隻要keepAliveTime值非0,能夠經過allowallowCoreThreadTimeOut(boolean value)     方法將超時策略應用於核心線程。

  四、unit

    keepAliveTime存活時間的單位。

  五、workQueue

    用來保存等待被執行的任務的阻塞隊列。JDK實現的隊列有如下幾種:

      ArrayBlockingQueue:基於數組結構的有界阻塞隊列,按照FIFO ( 先進先出 ) 排序任務。

      LinkedBlockingQueue:基於鏈表結構的阻塞隊列,按照FIFO ( 先進先出 ) 排序任務。效果比ArrayBlockingQueue好。

      DelayQueue:具備延時執行的無界阻塞隊列。

      SynchronousQueue:一個不能存儲任何元素的阻塞隊列,每一個插入的操做必須等待另外一個相應的刪除操做,不然插入則會一直處於阻塞狀態。

                第二個線程必須等待前一個線程結束。

      PriorityBlockingQueue:具備 優先級的無界阻塞隊列。

 

  六、threadFactory

    建立線程的工廠

    默認工廠實現:

  

 DefaultThreadFactory() {
            SecurityManager s = System.getSecurityManager();
            group = (s != null) ? s.getThreadGroup() :
                                  Thread.currentThread().getThreadGroup();
            namePrefix = "pool-" +
                          poolNumber.getAndIncrement() +
                         "-thread-";
        }

 

  七、handler

      線程池的飽和策略,用於線程數飽和與阻塞隊列已滿的狀況。有任務繼續提交時觸發的處理邏輯。線程池提供了4種策略:

      AbortPolicy:直接跑出異常,默認策略。

      DiscardPolicy:直接丟棄任務,不處理。

      DiscardOldestPolicy:丟棄阻塞隊列中靠前的舊的任務,執行新的任務。

      CallerRunsPolicy:用調用者的線程來執行任務。

相關文章
相關標籤/搜索