深入学习线程池

为什么会出现线程池?

在实际业务中使用多线程去处理业务,主要是为了将资源利用最大化,从而提高效率。但是我们上一节讲到了线程的创建与销毁,若频繁的创建线程就会大大降低系统的效率,因为频繁创建线程和销毁线程需要时间。此时我们会想有没有线程可以进行复用,就是创建一次可以在多个任务中使用,并不被销毁操作?
通过API,我们可以知道满足以上条件的正是本章节需要学习的ThreadPoolExecutor类。

了解ThreadPoolExecutor

通过查看源码,发现ThreadPoolExecutor类,有七个参数支撑,首先了解一下这七个参数:
在这里插入图片描述
corePoolSize:核心池的大小,这个参数跟后面讲述的线程池的实现原理有非常大的关系。在创建了线程池后,默认情况下,线程池中并没有任何线程,而是等待有任务到来才创建线程去执行任务,除非调用了prestartAllCoreThreads()或者prestartCoreThread()方法,从这2个方法的名字就可以看出,是预创建线程的意思,即在没有任务到来之前就创建corePoolSize个线程或者一个线程。默认情况下,在创建了线程池后,线程池中的线程数为0,当有任务来之后,就会创建一个线程去执行任务,当线程池中的线程数目达到corePoolSize后,就会把到达的任务放到缓存队列当中。
在这里插入图片描述
线程是否启动?
在这里插入图片描述
maximumPoolSize:线程池最大线程数,这个参数也是一个非常重要的参数,它表示在线程池中最多能创建多少个线程。

keepAliveTime:表示线程没有任务执行时最多保持多久时间会终止。即:超出corePoolSize的线程数,进行等待执行线程的等待时间,超出这个时间就会终止这个线程。

unit:参数keepAliveTime的时间单位,在TimeUnit类中有7种静态属性,如下:

TimeUnit.DAYS;               //天
TimeUnit.HOURS;             //小时
TimeUnit.MINUTES;           //分钟
TimeUnit.SECONDS;           //秒
TimeUnit.MILLISECONDS;      //毫秒
TimeUnit.MICROSECONDS;      //微妙
TimeUnit.NANOSECONDS;       //纳秒

workQueue:一个阻塞队列,用来存储等待执行的任务,这个参数的选择也很重要,会对线程池的运行过程产生重大影响,参考网上给出的策略如下所示:

ArrayBlockingQueue; // 是一个基于数组结构的有界阻塞队列,此队列按 FIFO(先进先出)原则对元素进行排序。
LinkedBlockingQueue; // 一个基于链表结构的阻塞队列,此队列按FIFO (先进先出) 排序元素,吞吐量通常要高于ArrayBlockingQueue。静态工厂方法Executors.newFixedThreadPool()使用了这个队列
SynchronousQueue; // 一个不存储元素的阻塞队列。每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量通常要高于LinkedBlockingQueue; // 静态工厂方法Executors.newCachedThreadPool使用了这个队列。
PriorityBlockingQueue; // 一个具有优先级的无限阻塞队列。

一般情况下,会默认使用LinkedBlockingQueue。

threadFactory:线程工厂,主要用来创建线程。我会默认填写Executors.defaultThreadFactory()。

handler:表示当拒绝处理任务时的策略,有四种取值,参考网上如下所示:

ThreadPoolExecutor.AbortPolicy;  // 丢弃任务并抛出RejectedExecutionException异常。 
ThreadPoolExecutor.DiscardPolicy; // 也是丢弃任务,但是不抛出异常。 
ThreadPoolExecutor.DiscardOldestPolicy; // 丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)
ThreadPoolExecutor.CallerRunsPolicy; // 由调用线程处理该任务 

线程池的主要操作方法如下所示:

execute:只能接受Runnable类型的任务

submit:不管是Runnable还是Callable类型的任务都可以接受,但是Runnable返回值均为void,所以使用Future的get()获得的还是null

shutdown:关闭线程池

shutdownNow:立即关闭线程池

Executors四种线程池

前面我们刚刚学习了ThreadPoolExecutor线程池类,是最底层的线程池的构造。通过源码可知道,Executors使用的线程池,底层都是ThreadPoolExecutor类。
如下图所示:
在这里插入图片描述

Executors.newCachedThreadPool()

解释:
可伸缩,根据CPU调整线程数量,遇强则强,遇弱则弱
在这里插入图片描述

Executors.newSingleThreadExecutor()

解释:
单个线程在这里插入图片描述

Executors.newFixedThreadPool(int nThreads )

解释:
创建一个固定的线程池大小
在这里插入图片描述

Executors.newScheduledThreadPool(int corePoolSize)

解释:
是一个可以周期性执行任务的线程池。通俗的讲,就是指定时间执行任务!
在这里插入图片描述
在这里插入图片描述
举例说明周期性执行的线程池:

public static void main(String[] args) {
        SimpleDateFormat simpleFormatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        ScheduledExecutorService pool = Executors.newScheduledThreadPool(1);
        System.out.println(simpleFormatter.format(new Date()));
        pool.schedule(()->{
            //定时任务
            System.out.println("五秒后执行!" + simpleFormatter.format(new Date()));
        }, 5, TimeUnit.SECONDS);
        pool.shutdown();
    }

执行结果如下:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

胤墨

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值