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;         }       }      }  

相关文章
相关标签/搜索