文章目录

  • 线程池
  • 核心线程池
  • 定长线程池(FixedThreadPool)
  • 定时线程池(ScheduledThreadPool )
  • 可缓存线程池(CachedThreadPool)
  • 单线程化线程池(SingleThreadExecutor)
  • 总结
  • 对其进行优化
  • 七个参数
  • 线程池最大线程数目优化


线程池

  • 程序的运行要创建进程, 进程中任务的执行要创建线程去执行, 而频繁的创建线程是很耗费资源的,为了优化资源就采用了池化技术, 比如线程池和数据库连接池
  • 池化技术就是事先准备好一些资源, 有人要拿就去用, 没人拿就放那
  • 降低了创建和销毁的效率, 提高了响应的速度, 方便了管理(可以控制最大并发数, 管理线程),实现了程序的复用

核心线程池

定长线程池(FixedThreadPool)

public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }
  • nThreads在初始化的时候就订好了, 并且他的核心线程和最大线程数目是相同的, 主要用在服务器上, 可以控制最大的并发数
  • 但是他有缺点他的任务队列BlockIngQueue的允许长度了Integer.MAX_VALUE, 可能会堆积大量的请求, 从而导致OOM

定时线程池(ScheduledThreadPool )

public static ScheduledExecutorService newScheduledThreadPool(
        int corePoolSize, ThreadFactory threadFactory) {
    return new ScheduledThreadPoolExecutor(corePoolSize, threadFactory);
  • 核心线程也是固定的, 最大线程数量是无限的, 可以执行定时任务和周期任务
  • 缺点就是因为其最大线程是Integer.MAX_VALUE 可能会创建大量的线程, 从而导致OOM

可缓存线程池(CachedThreadPool)

public static ExecutorService newCachedThreadPool() {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                  60L, TimeUnit.SECONDS,
                                  new SynchronousQueue<Runnable>());
  • 无核心线程, 并且其最大线程数目也是无限的, 默认是60s后回收, 任务队列为不存储任务的阻塞队列, 用于执行量大, 任务短小的场景
  • 缺点就是因为其最大线程是Integer.MAX_VALUE 可能会创建大量的线程, 从而导致OOM

单线程化线程池(SingleThreadExecutor)

public static ExecutorService newSingleThreadExecutor() {
    return new FinalizableDelegatedExecutorService
        (new ThreadPoolExecutor(1, 1,
                                0L, TimeUnit.MILLISECONDS,
                                new LinkedBlockingQueue<Runnable>()));
  • 核心线程和最大线程都是1, 用完即可回收线程, 任务队列为链表结构的有界队列, 用于不适合并发,但可能有阻塞的场景, 或者一些短小任务
  • 缺点还是他的任务列表的最大值还是Integer.MAX_VALUE, 可能会堆积大量的请求, 从而导致OOM

总结

  1. FixedThreadPool和SingleThreadPool中的LinkedBlockIngQueue的默认允许长度为Integer.MAX_VALUE, 可能会堆积大量的请求, 从而导致OOM

LinkedBlockingQueue: 一个由链表结构组成的有界阻塞队列,在未指明容量时,容量默认为Integer.MAX_VALUE。

  1. CachedThreadPool和ScheduledThreadPool允许创建的线程的最大值是Integer.MAX_VALUE 可能会创建大量的线程, 从而导致OOM

SynchronousQueue(CachedThreadPool使用):一个不存储元素的阻塞队列,消费者线程调用take()方法的时候就会发生阻塞,直到有一个生产者线程生产了一个元素,消费者线程就可以拿到这个元素并返回;生产者线程调用put()方法的时候也会发生阻塞,直到有一个消费者线程消费了一个元素,生产者才会返回。

对其进行优化

  • 通过源码我们发现, 上述在构建线程的时候都是使用ThreadPoolExecutor构建, 那我们就对我们实际的需求来设计一个满足我们自身的线程池
  • 但是我们发现如果自定义会有7个参数, 如果想要自定义线程池就必须知道这7个参数的意思
public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler)

七个参数

public ThreadPoolExecutor(int corePoolSize, //核心线程数目
                          int maximumPoolSize,  //最大线程数目
                          long keepAliveTime,   //线程保活时间
                          TimeUnit unit,  //保活时间单位
                          BlockingQueue<Runnable> workQueue, //存放任务的阻塞队列
                          ThreadFactory threadFactory,  //线程工厂, 创建线程需要使用, 一般不用修改
                          RejectedExecutionHandler handler //拒绝策略) {
  1. corePoolSize(必需):核心线程数。默认情况下,核心线程会一直存活,但是当将allowCoreThreadTimeout设置为true时,核心线程也会超时回收。
  2. maximumPoolSize(必需):线程池所能容纳的最大线程数。当活跃线程数达到该数值后,后续的新任务将会阻塞。
  3. keepAliveTime(必需):线程闲置超时时长。如果超过该时长,非核心线程就会被回收。如果将allowCoreThreadTimeout设置为true时,核心线程也会超时回收。
  4. unit(必需):指定keepAliveTime参数的时间单位。常用的有:TimeUnit.MILLISECONDS(毫秒)、TimeUnit.SECONDS(秒)、TimeUnit.MINUTES(分)。
  5. workQueue(必需):任务队列。通过线程池的execute()方法提交的Runnable对象将存储在该参数中。其采用阻塞队列实现。
  6. threadFactory(可选):线程工厂。用于指定为线程池创建新线程的方式。(一般都是使用默认的)
  7. handler(可选):拒绝策略。当达到最大线程数并且任务对队列也满了时需要执行的饱和策略。
  • 通过执行流程图来理解参数的意思
  • 线程工厂(threadFactory), 线程工厂指定创建线程的方式,需要实现ThreadFactory接口,并实现newThread(Runnable r)方法。该参数可以不用指定,Executors框架已经为我们实现了一个默认的线程工厂:
  • 拒绝策略:
  1. 线程池中默认的是AbortPolicy
  • 这个就是当最大线程数满了,再加上阻塞队列中等待的任务也满了, 就出抛出异常, 通知我不要了!
  1. CallerRunPolicy
  • 当就是当最大线程数满了,再加上阻塞队列中等待的任务也满了, 就将这个任务返回给传递者, 让传递者去执行这个任务
  1. DiscardPolicy
  • 当都满了的时候, 就忽略掉这个任务
  1. DiscardOldestPolicy
  • 这个都满了的时候他会排在对前面的那个任务快要执行完了,尝试去和他竞争一下, 如果竞争不上就会忽略这个任务, 不会抛出异常

线程池最大线程数目优化

  1. CPU密集型, 按照CPU的数目去实现最大线程的个数, 使用RunTime.getRunTime().availableProcessprs()去获取参数
  2. IO密集型, 判断可能会有多少个大任务, 比如有10个十分占用资源的任务, 那最大线程的数目一定是大于这个大任务的