ExecutorService详解

76 阅读4分钟

ExecutorService是Java提供的线程池,也就是说,每次我们需要使用线程的时候,可以通过ExecutorService获得线程,它可以有效控制最大并线程数,提高系统资源的使用率,同时避免过多资源竞争,避免堵塞,同时提供定时执行,定期执行,并发数控制等功能,也不用使用TimerTesk了。

ExecutorService创建方式

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler)

所有线程池最终都是通过这个方法来创建的。

  • corePoolSize : 核心线程数,一旦创建将不会再释放。如果创建的线程数还没有达到指定的核心线程数量,将会继续创建新的核心线程,直到达到最大核心线程数后,核心线程数将不在增加;如果没有空闲的核心线程,同时又未达到最大线程数,则将继续创建非核心线程;如果核心线程数等于最大线程数,则当核心线程都处于激活状态时,任务将被挂起,等待空闲线程来执行。
  • maximumPoolSize : 最大线程数,允许创建的最大线程数量。如果最大线程数等于核心线程数,则无法创建非核心线程;如果非核心线程处于空闲时,超过设置的空闲时间,则将被回收,释放占用的资源。
  • keepAliveTime : 也就是当线程空闲时,所允许保存的最大时间,超过这个时间,线程将被释放销毁,但只针对于非核心线程。
  • unit : 时间单位,TimeUnit.SECONDS等。
  • workQueue : 任务队列,存储暂时无法执行的任务,等待空闲线程来执行任务。
  • threadFactory : 线程工厂,用于创建线程。
  • handler : 当线程边界和队列容量已经达到最大时,用于处理阻塞时的程序,也就是拒绝策略

线程池的类型

1、可缓存线程池

ExecutorService cachePool = Executors.newCachedThreadPool();

在看一看事项的方式,其实也是用ThreadPoolExecutor创建的

public static ExecutorService newCachedThreadPool() {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                  60L, TimeUnit.SECONDS,
                                  new SynchronousQueue<Runnable>());
}

通过它的创建方式可以知道,创建的都是非核心线程,而且最大线程数为Interge的最大值,空闲线程存活时间是1分钟。如果有大量耗时的任务,则不适该创建方式。它只适用于生命周期短的任务

2、单线程池

ExecutorService singlePool = Executors.newSingleThreadExecutor();

顾名思义,也就是创建一个核心线程:

public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }

只用一个线程来执行任务,保证任务按FIFO顺序一个个执行。

3、固定线程数线程池

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

创建固定数量的可复用的线程数,来执行任务。当线程数达到最大核心线程数,则加入队列等待有空闲线程时再执行

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

基本使用方法:

ScheduledExecutorService pool1=Executors.newScheduledThreadPool(3);
//延迟执行任务
pool1.schedule(new MyRunnable(),10,TimeUnit.SECONDS);
//定时执行任务,没隔多长时间久执行一次
//第一次打印出来是用来2s中,以后开始每5s打印一次。
pool1.scheduleAtFixedRate(new Runnable() {
    @Override
    public void run() {
        System.out.println("定时任务执行了!!!");
    }
},2,5,TimeUnit.SECONDS);

scheduleAtFixedRate和sheduleWithFixedDelay有什么不同呢?

它们都可以延时且定期执行任务,但延时的时间是有差别的

  • scheduleAtFixedRate,中文意思为 以固定比率执行,参数有 Runnable command, long initialDelay,long period,TimeUnit unit 第1次执行的时间是initialDelay(unit),第2次执行的时间是initialDelay+period(unit),第3次执行的时间是initialDelay+period*2(unit),依次类推。。。也就是,在任务开始后,period时间后,便会执行下一次任务。如果period时间到了,但上一次还没执行完毕,则等待,直到上一次的任务执行完毕,然后马上执行本次任务。
  • scheduleWithFixedDelay ,中文意思为 以固定的延迟来执行,参数有 Runnable command, long initialDelay,long delay,TimeUnit unit ,该方法第1次执行也是在initialDelay(unit)后,但第2次执行是在第1次执行完毕后算起的delay时间后再执行。
  • 因此,这两个方法的不同点是,scheduleAtFixedRate 是从任务开始时算起, scheduleWithFixedDelay 是从任务结束时算起。。