ExecutorService詳解

前言
        在咱們的平常開發中,不免會使用到線程,部分還會用到多線程併發問題。咱們知道,線程的建立和釋放,須要佔用不小的內存和資源。若是每次須要使用線程時,都new 一個Thread的話,不免會形成資源的浪費,並且能夠無限制建立,之間相互競爭,會致使過多佔用系統資源致使系統癱瘓。不利於擴展,好比如定時執行、按期執行、線程中斷,因此頗有必要了解下ExecutorService的使用。緩存

        ExecutorService是Java提供的線程池,也就是說,每次咱們須要使用線程的時候,能夠經過ExecutorService得到線程。它能夠有效控制最大併發線程數,提升系統資源的使用率,同時避免過多資源競爭,避免堵塞,同時提供定時執行、按期執行、單線程、併發數控制等功能,也不用使用TimerTask了。多線程

1.ExecutorService的建立方式
public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler)
全部線程池最終都是經過這個方法來建立的。併發

corePoolSize : 核心線程數,一旦建立將不會再釋放。若是建立的線程數尚未達到指定的核心線程數量,將會繼續建立新的核心線程,直到達到最大核心線程數後,核心線程數將不在增長;若是沒有空閒的核心線程,同時又未達到最大線程數,則將繼續建立非核心線程;若是核心線程數等於最大線程數,則當核心線程都處於激活狀態時,任務將被掛起,等待空閒線程來執行。post

maximumPoolSize : 最大線程數,容許建立的最大線程數量。若是最大線程數等於核心線程數,則沒法建立非核心線程;若是非核心線程處於空閒時,超過設置的空閒時間,則將被回收,釋放佔用的資源。this

keepAliveTime : 也就是當線程空閒時,所容許保存的最大時間,超過這個時間,線程將被釋放銷燬,但只針對於非核心線程。.net

unit : 時間單位,TimeUnit.SECONDS等。線程

workQueue : 任務隊列,存儲暫時沒法執行的任務,等待空閒線程來執行任務。code

threadFactory :  線程工程,用於建立線程。生命週期

handler : 當線程邊界和隊列容量已經達到最大時,用於處理阻塞時的程序隊列

2.線程池的類型
2.1 可緩存線程池
ExecutorService cachePool = Executors.newCachedThreadPool();
   看看它的具體建立方式:

public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }
    經過它的建立方式能夠知道,建立的都是非核心線程,並且最大線程數爲Interge的最大值,空閒線程存活時間是1分鐘。若是有大量耗時的任務,則不適該建立方式。它只適用於生命週期短的任務。

2.2 單線程池
ExecutorService singlePool = Executors.newSingleThreadExecutor();
顧名思義,也就是建立一個核心線程:

public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }
只用一個線程來執行任務,保證任務按FIFO順序一個個執行。

2.3 固定線程數線程池
Executors.newFixedThreadPool(3);
public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }
也就是建立固定數量的可複用的線程數,來執行任務。當線程數達到最大核心線程數,則加入隊列等待有空閒線程時再執行。

2.4 固定線程數,支持定時和週期性任務
ExecutorService scheduledPool = Executors.newScheduledThreadPool(5);
public ScheduledThreadPoolExecutor(int corePoolSize) {
    super(corePoolSize, Integer.MAX_VALUE,
          DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
          new DelayedWorkQueue());
}
可用於替代handler.postDelay和Timer定時器等延時和週期性任務。

public ScheduledFuture<?> schedule(Runnable command,
                                       long delay, TimeUnit unit);
public ScheduledFuture<?> scheduleAtFixedRate(Runnable command,
                                                  long initialDelay,
                                                  long period,
                                                  TimeUnit unit);
public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command,
                                                     long initialDelay,
                                                     long delay,
                                                     TimeUnit unit);
scheduleAtFixedRate和sheduleWithFixedDelay有什麼不一樣呢?

scheduleAtFixedRate:建立並執行一個在給定初始延遲後的按期操做,也就是將在 initialDelay 後開始執行,而後在initialDelay+period 後下一個任務執行,接着在 initialDelay + 2 * period 後執行,依此類推 ,也就是隻在第一次任務執行時有延時。

sheduleWithFixedDelay:建立並執行一個在給定初始延遲後首次啓用的按期操做,隨後,在每一次執行終止和下一次執行開始之間都存在給定的延遲,即總時間是(initialDelay +  period)*n

2.4 手動建立線程池
private ExecutorService pool = new ThreadPoolExecutor(3, 10,
            10L, TimeUnit.SECONDS,
            new LinkedBlockingQueue<Runnable>(512), Executors.defaultThreadFactory(),
            new ThreadPoolExecutor.AbortPolicy());
能夠根據本身的需求建立指定的核心線程數和總線程數。

3. 如何終止一個週期性任務呢?
直接上源碼你就懂了:

public final class LgExecutorService {
 
    private ConcurrentHashMap<String, Future> futureMap = new ConcurrentHashMap<>();
 
    private ScheduledExecutorService executorService = new ScheduledThreadPoolExecutor(5);
 
 
    private LgExecutorService() {
 
    }
 
    private static final class InnerExecutorService {
        private static final LgExecutorService INSTANCE = new LgExecutorService();
    }
 
    public static LgExecutorService getInstance() {
        return InnerExecutorService.INSTANCE;
    }
 
    public ConcurrentHashMap<String, Future> getFutureMap() {
        return futureMap;
    }
 
    public void execute(Runnable runnable) {
        if (runnable != null) {
            executorService.execute(runnable);
        }
    }
 
    /**
     * @param runnable
     * @param delay    延遲時間
     * @param timeUnit 時間單位
     */
    public void sheduler(Runnable runnable, long delay, TimeUnit timeUnit) {
        if (runnable != null) {
            executorService.schedule(runnable, delay, timeUnit);
        }
    }
 
    /**
     * 執行延時週期性任務
     *
     * @param runnable {@code LgExecutorSercice.JobRunnable}      * @param initialDelay 延遲時間      * @param period       週期時間      * @param timeUnit     時間單位      */     public <T extends JobRunnable> void sheduler(T runnable, long initialDelay, long period, TimeUnit timeUnit) {         if (runnable != null) {             Future future = executorService.scheduleAtFixedRate(runnable, initialDelay, period, timeUnit);             futureMap.put(runnable.getJobId(), future);         }     }       public static abstract class JobRunnable implements Runnable {           private String jobId;           public JobRunnable(@NonNull String jobId) {             this.jobId = jobId;         }           /**          * 強制終止定時線程          */         public void terminal() {             try {                 Future future = LgExecutorService.getInstance().getFutureMap().remove(jobId);                 future.cancel(true);             } finally {                 System.out.println("jobId " + jobId + " had cancel");             }         }           public String getJobId() {             return jobId;         }       }      }  

相關文章
相關標籤/搜索