ExecutorService-内置线程池(三)

108 阅读4分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第19天,点击查看活动详情

1. 内置线程池

共四种:

  • newCachedThreadPool
  • newFixedThreadPool
  • newScheduledThreadPool
  • newSingleThreadExecutor

1.1 newCachedThreadPool

ExecutorService es = Executors.newCachedThreadPol()

  • 只有临时线程没有核心线程
  • 临时线程的数量是:Integer.MAX_VALUE,所以可以认为临时线程是无限多的
  • 每一个临时线程在用完后能存活1分钟
  • 工作队列是一个同步队列(SynchronousQueue
  • ==是一个大池子小队列的线程池==
  • 适用于大量的短任务高并发的场景,不适用于长任务场景

初始方法:

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

1.2 newFixedThreadPool

ExecutorService es = Executors.newFixedThreadPool(int nThreads);

  • 只有核心线程没有临时线程
  • 工作队列是一个阻塞式链式队列,且该队列没有指定容量,所以容量是:Integer.MAX_VALUE,认为可以存储无限多个请求
  • ==是一个大队列小池子线程池==
  • 适用于长任务场景,不适用于短任务场景

初始方法:

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

1.3 newScheduledThreadPool

scheduled - 预定的

ScheduledExecutorService ses = Executors.newScheduledThreadPool(5);

定义调度执行器。能够起到定时执行的效果,很多定时机制的底层都是利用这个线程池来实现的

初始方法

public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
    return new ScheduledThreadPoolExecutor(corePoolSize);
}

使用案例

scheduleAtFixedRate:从上次线程启动开始,计算下一次线程启动的时间

  • 如果线程的执行时间超过时间间隔,则以线程执行时间为准

scheduleWithFixedDelay:从上次线程结束开始,计算下一次线程启动的时间

public class ScheduleExecutorServiceDemo {
    public static void main(String[] args) {
        ScheduledExecutorService ses = Executors.newScheduledThreadPool(5);
        
        //正常延迟5s后执行,只执行一次
        ses.schedule(new ScheduleThread(), 5, TimeUnit.SECONDS);

        // 每隔5s钟执行一次
        // initialDelay->延迟几秒启动
        // period->线程延迟时间
        // 从上一次启动开始,来计算下一次的启动时间
        // 如果线程的执行时间超过时间间隔,则以线程执行时间为准
        ses.scheduleAtFixedRate(new ScheduleThread(), 0, 5, TimeUnit.SECONDS);

        // 每隔5s执行一次
        // 从上一次线程结束开始,来计算下一次启动的时间
        ses.scheduleWithFixedDelay(new ScheduleThread(), 0, 5, TimeUnit.SECONDS);
    }
}

class ScheduleThread implements Runnable{

    @Override
    public void run() {
        try {
            System.out.println("hello");
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

1.4 newSingleThreadExecutor

ExecutorService es = Executors.newSingleThreadExecutor();

这个线程池只有一个线程 这个线程池可以在线程死后(或发生异常时)重新启动一个线程来替代原来的线程继续执行下去!

1.4 线程池参数

  1. 核心线程数(corePoolSize):核心线程数的设计需要依据任务的处理时间和每秒产生的任务数量来确定,例如:执行一个任务需要0.1秒,系统百分之80的时间每秒都会产生100个任务,那么要想在1秒内处理完这100个任务,就需要10个线程此时我们就可以设计核心线程数为10;当然实际情况不可能这么平均所以我们一般按照8O20原则设计即可,既按照百分之80的情况设计核心线程数,剩下的百分之20可以利用最大线程数处理;

  2. 任务队列长度(workQueue):任务队列长度一般设计为:核心线程数/单个任务执行时间*2即可;例如上面的场景中,核心线程数设计为10,单个任务执行时间为0.1秒,则队列长度可以设计为200;

  3. 最大线程数(maximumPoolSize):最大线程数的设计除了需要参照核心线程数的条件外;还需要参照系统每秒产生的最大任务数决定:例如:上述环境中,如果系统每秒最大产生的任务是1000个,那么,最大线程数=(最大任务数-任务队列长度)*单个任务执行时间;既:最大线程数=(1000-200)*0.1=80个;

  4. 最大空闲时间(keepAliveTime):这个参数的设计完全参考系统运行环境和硬件压力设定,没有固定的参考值,用户可以根据经验和系统产生任务的时间间隔合理设置一个值即可;