多线程基础(五)线程池

1. ThreadPoolExecutor
虽然Executors框架提供了好多创建线程的方法,比如:
在这里插入图片描述
但是我们并不推荐大家使用这些方法,阿里开发规范也有一条:创建线程池建议大家使用ThreadPoolExecutor类,我们看一下Executors源码这些创建方式底层都是对ThreadPoolExecutor类的封装

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

所以本文我们直接讲解ThreadPoolExecutor的使用

2. ThreadPoolExecutor的参数

ThreadPoolExecutor类的构造方法有多个但是其实最后调用的都是同一个:

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

int corePoolSize:----------------线程池中核心线程数
int maximumPoolSize,:--------线程池中非核心线程数
long keepAliveTime,:-----------当线程池中的线程数超过核心线程数,多余的空闲非核心线程多长时间会被销毁
TimeUnit unit,:-------------------参数keepAliveTime的时间单位
BlockingQueue,:-----------------任务队列,被提交但尚未被执行的任务
ThreadFactory threadFactory,:线程工厂,一般默认即可
RejectedExecutionHandler:—拒绝策略,当线程太多来不及处理时如何拒绝任务
线程工厂一般不需要自定义,当然你也可以自定义自己的线程工厂,下面我们重点讲一下任务队列和拒绝策略

3. 任务队列

  • 直接提交的队列:SynchronousQueue
    SynchronousQueue是一个特殊的队列,它没有容量,每一个插入操作都要等待一个删除操作,每一个删除操作要等待一个插入操作,说白了就是一对一的生产者消费者模式。它不保存任务,总是将任务直接交给线程处理如果线程数到达核心线程数则直接创建非核心线程数,如果线程数达到做大线程数则执行拒绝策略。
    - 有界任务队列:ArrayBlockingQueue
    ArrayBlockingQueue构造函数必须要带一个容量参数,标识该队列的最大容量;如果线程数未达到核心线程数,则创建核心线程执行任务,如果线程数到达coreSize,则把任务保存在任务队列中等待空闲的核心线程处理,如果队列放满了就在不超过maximumPoolSize的前提下创建非核心线程,如果线程数达到最大队列也满了则执行拒绝策略。
    - 无界任务队列:LinkedBlockingQueue
    LinkedBlockingQueue,相当于没有容量限制的有界队列,所以线程数不会超过核心线程数,当任务量很多而且处理缓慢时队列容量会无限增长直到耗尽系统内存。
    - 优先任务队列:PriorityBlockingQueue
    PriorityBlockingQueue是一个特殊的无界队列,区别是前面两种队列都是按照先进先出的顺序消费队列中等待的任务,而PriorityBlockingQueue会根据线程的优先级,限制性优先级高的任务,可以理解为优先任务队列里面保存的数据是排序后的数据并不是按照先进先出。排序是按照Comparable接口的compareTo方法来比较的。

无界队列构造方法可以传入容量参数,此时相当于有界队列

4. 拒绝策略
拒绝策略是当任务数量超过线程池承载能力时决定任务应如何处理,线程池内置四种拒绝策略
new ThreadPoolExecutor.AbortPolicy():直接抛出异常,阻止系统正常工作
new ThreadPoolExecutor.CallerRunsPolicy():只要线程池未关闭,该策略会直接在调用者线程当前被丢弃的任务。
new ThreadPoolExecutor.DiscardOldestPolicy():丢弃一个最老的任务(也就是即将被执行的任务),并尝试再次提交当前任务
new ThreadPoolExecutor.DiscardPolicy():默默丢弃当前任务,不予任何处理

5. 自定义拒绝策略
可以自己实现RejectedExecutionHandler接口,自定义拒绝策略。

7. 线程池线程数大小估算(最优的线程数需要进行实际测试才能确定)
Ncpu:CPU数量
Ucpu:cpu目标使用率,[0,1]
W/C:等待时间与计算时间的比
估算公式:Ncpu * Ucpu * (1 + W/C)

8. 线程池关闭
shutdown();
shutdownNow();
shutdown只是将线程池的状态设置为SHUTWDOWN状态,正在执行的任务会继续执行下去,没有被执行的则中断。而shutdownNow则是将线程池的状态设置为STOP,正在执行的任务则被停止,没被执行任务的则返回。

发布了52 篇原创文章 · 获赞 7 · 访问量 3817

猜你喜欢

转载自blog.csdn.net/maomaoqiukqq/article/details/99696562