线程池详解

构造参数

线程池ThreadPoolExecutor的部分构造参数说明:

  • corePoolSIze: 核心线程数目
  • runnableTaskQueue: 阻塞队列,保存等待执行的任务。有以下几种类型:
    • ArrayBlockingQuque: 基于数组实现的有界阻塞队列, 队列大小由构造函数指定
    • LinkedBlockingQueue: 基于链表实现的无界阻塞队列,吞吐量通常高于ArrayBlockingQuque(没验证)
    • SynchronousQueue: 不存储元素的阻塞队列。 该队列挺有意思, 有时间研究一下
    • PriorityBlockingQueue: 具有优先级的无界阻塞队列
  • maxPoolISize: 线程池的最大线程数目
  • RejectedExecutionHandler: 拒绝饱和策略,当队列和最大线程池都满的时候,若此时添加任务,由该策略来执行,默认情况是AbortPolicy, 表示直接抛出异常,也可以使用其他几种策略或者自定义策略
  • keepAliveTime: 当工作线程空闲之后,还能活多久. 当任务很多且每个任务执行的时间较短,则可以适当调高该参数,减少线程的创建. 此处杀死的线程是指当前线程数大于核心线程数时,才有可能去杀死超过核心线程数且小于最大线程数的这些线程

执行流程

线程池中有三个基本概念:

  • 核心线程数目
  • 线程队列
  • 最大线程数目

当往线程池中添加一个线程时,会执行如下判断流程:

  1. 首先判断线程池中线程数目是否小于规定的核心线程数,若是则会创建一个新的线程并执行该任务,若否则执行下一流程
  2. 判断线程池的队列是否已满,若队列未满,则将该任务加入队列中;若队列已满,则执行下一流程
  3. 判断线程池中线程数目是否小于规定的最大线程数,若是则会创建一个新的线程并执行该任务,若否则交由拒绝策略来进行处理.

具体流程如下图所示:

20200911104429

注意:

  1. 当提交一个线程到线程池中时,该线程首先会执行提交的任务,待执行完毕之后再循环的取队列中的任务去执行.
  2. 每次创建线程时都需要获取全局锁,包括创建核心线程和最大线程

下面这个代码如下:

public class ThreadPoolDemo {
    public static void main(String[] args) {
        ThreadPoolExecutor executor = new ThreadPoolExecutor(
                2, 5, 0L, TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(10)
        );
        new Thread(new Runnable() {
            @Override
            public void run() {
                while (true){
                    try {
                        TimeUnit.SECONDS.sleep(1);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("活动线程:" + executor.getActiveCount() + " 线程数量:" + executor.getPoolSize());
                }
            }
        }).start();

        for (int i=0; i < 20; i++){
            executor.submit(new TestRunnable(i));
        }
    }

    static class TestRunnable implements Runnable{
        public TestRunnable(int num) {
            this.num = num;
        }

        private int num;
        @Override
        public void run() {
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

其中有一个监控线程,每隔一秒会打印出线程池的情况。刚开始时打印出结果是:

活动线程:5 线程数量:5

此时线程池创建除了最大的5个线程, 且都在执行任务。

当所有任务全部都执行完毕之后, 打印结果是:

活动线程:0 线程数量:2

此时任务全部都执行完毕, 所以线程池内部只保留了2个核心线程, 其他线程已经全部被杀死。

几种特定线程池

FixedThreadPool

线程数是固定的线程池

其构造函数如下:

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

核心线程数和最大线程数相等,这样当往线程池添加任务时,若当前线程数小于核心线程数,则会新建线程,否则将任务放入无界队列中, 进行等待.该类型线程池少了一个当队列满之后,继续创建线程的步骤.

由以上可知, 该类型的线程池一旦线程数达到nThreads之后, 除非线程池被销毁, 否则线程池内将会一直有nThreads个线程存活。

该线程池的keepAliveTime设置为0我感觉没多大意义,因为核心线程数和最大线程数一样,所以线程池中不存在多余的空闲线程

SingleThreadExecutor

只有一个线程的线程池

线程池核心线程数和最大线程数都为1, 当往线程池中添加任务时,若线程池无运行线程,则会创建线程,否则加入无界队列中,等待线程从该队列中获取任务

ScheduledThreadPoolExecutor

具有定时任务功能的线程池

使用无界延迟队列DelayQueue.线程执行的任务都被封装成一个ScheduledFutureTask,添加定时任务时,会将这个ScheduledFutureTask放入到队列中。同时线程池中的线程会从队列中获取任务进行执行。

(1) 该线程池是怎样保证定时执行?

关键字: ScheduleFutureTask, PriorityQueue

主要是在ScheduleFutureTask类上,该类包含三个成员变量:

  • 任务将要被执行的具体时间time
  • 任务被添加到线程池时的序号number
  • 任务执行的间隔周期period

队列DelayQueue内部封装了一个优先级队列PriorityQueue,该队列会对其中所有的ScheduleFutureTask进行排序,time越小排序越靠前。线程从队列中获取到期任务(即当前时间小于等于time),执行完成之后,再根据period重新设置time,然后将该任务重新放入队列中。

CachedThreadPool

根据需要创建新线程的线程池

其构造方法如下:

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

由构造方法可知, 当有新任务进来时, 如果线程池有空闲线程(所以此处设置的线程存活时间较长,为60s),则使用该线程, 否则就会一直创建新线程来执行(因为使用了SynchronousQueue队列, 该队列不存储元素, 所以不会有任务被存储到队列中等待)。所以当任务处理速度比较缓慢时, 有可能会导致一直创建新线程, 最终导致CPU和内存资源耗尽。一般不适用该线程池。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值