线程池介绍

2020-07-18  本文已影响0人  一直在路上_求名

概述

多线程并行处理任务是压榨 CPU 的最有效方式,而线程在执行完任务后如果直接销毁,这个是对资源的浪费,于是就有了池化的概念;
创建一个包含多个线程的池,当需要用到线程是是直接使用池中的线程,而不是单独在创建一个线程,从而避免了线程重复创建和销毁的开销,提高了资源的利用率;
线程池是通过生成者消费者模式构建的,当我们使用线程池的 submit 方法提交任务时,就是担任了生产者的角色来提交任务,当线程池中的线程执行队列中的任务时就是担任类消费者的模式消费任务;
在 JDK 中提供了 ThreadPoolExecutor 类用来实现线程池。

线程池参数介绍

首先给出创建线程池类的构造方法:

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {
        if (corePoolSize < 0 ||
            maximumPoolSize <= 0 ||
            maximumPoolSize < corePoolSize ||
            keepAliveTime < 0)
            throw new IllegalArgumentException();
        if (workQueue == null || threadFactory == null || handler == null)
            throw new NullPointerException();
        this.acc = System.getSecurityManager() == null ?
                null :
                AccessController.getContext();
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }

可以看到构造方法中入参有7个,接下来分别介绍;

corePoolSize

线程池的核心线程数;所谓核心线程数就是线程池通常情况下维护的最大线程数,而通常情况指的是当 workQueue 未满,也就是只要任务队列未满,线程池中最大线程数就是核心线程数;具体的执行过程是,当线程池中线程数未达到 corePoolSize 时,一个任务被提交后线程池会先创建一个线程去执行该任务,当线程池中线程数达到了 corePoolSize 后,一个任务被提交后会放入到任务队列中,只要队列未满,线程池中最大线程数就是 corePoolSize;

maximumPoolSize

最大线程数;这个就是真正意义上的线程池中所能创建线程的最大值了,只有当任务队列已满,有新任务被提交后线程池才会创建新线程来执行任务,这个时候线程池的线程数量会超过 corePoolSize 的大小,当线程池的线程数达到 maximumPoolSize 时,线程池会执行 handler 里的拒绝策略;

keepAliveTime & unit

线程的保留时间和时间单位;这个针对的是非核心线程而言的,因为当队列满了之后才会创建非核心线程之外的额外线程来处理任务,但是当任务处理完成队列又有容量的时候,说明线程池是处在闲置的状态,而多余的线程就是一种资源的浪费,所以线程池会将多余的线程移除并销毁,而这个保留时间就是指当线程池处于闲置状态时,多余的线程在池中最大的保留时间,超过这个时间后,多余的线程就会被销毁;

threadFactory

线程池中创建线程的工厂;这个其实没啥好介绍的,就是一个线程池创建的工厂,可以自定义一个线程工厂,给线程设置自定义的名称等,方便查日志及定位问题;

workQueue

线程池中用来保存提交任务的阻塞队列;上文中提到线程池是基于生产者消费者模型构建的,因此就需要用到阻塞队列来存储对应的任务,生产者把任务提交到队列,消费者从队列中获取任务进行执行;在 JDK 中提供了几种阻塞队列;
ArrayBlockingQueue:使用数组实现的阻塞队列因此构造时必须要传队列的大小,因此是一个有界的队列;
LinkedBlockingQueue:使用链表实现的阻塞队列,如果指定大小则容量以指定大小为准否则取 int 的最大值;
SynchronousQueue:内部只能包含一个元素的同步队列,能保证两个线程同步执行;
PriorityBlockingQueue : 使用平衡二叉树堆实现的优先级队列无界阻塞队列,每次都返回优先级最低或最高的元素;

handler

线程池中的拒绝策略;这个拒绝策略其实是一个兜底的方案,因为线程池是有界的,有最大线程数的限制也有最大队列容量的限制,当超过线程池本身的限制时,我们提交的任务线程池其实是没有能力再去处理的,这个时候就需要一个兜底的方案去做这件事情,而拒绝策略就是用来做这个的;在 JDK 中为我们提供了几种拒绝策略,我们也可以去自己实现拒绝策略,下面介绍一下JDK 提供的拒绝策略;
AbortPolicy(默认):丢弃当前任务并抛出 RejectedExecutionException 异常;
DiscardPolicy:丢弃当前任务,不抛出异常;
DiscardOldestPolicy:丢弃队列中最旧的任务,然后将当前任务加入队列;
CallerRunsPolicy:使用用调用者的线程继续执行任务;

上一篇下一篇

猜你喜欢

热点阅读