《多线程系列四》解密线程池的所作所为

169 阅读4分钟

「这是我参与2022首次更文挑战的第16天,活动详情查看:2022首次更文挑战

好几天没写,最近有点烦躁,不知道从何开始,也不知道从哪里结束,真烦。虽然贼烦还是得继续。加油。

今天继续多线程系列,Java开发常用的不可避免的多线程问题,多线程问题一般就是锁,线程,线程池。

今天大概聊聊Java的线程池,我们游戏里有不同的线程池使用场景,有邮件的线程池,有入库的线程池,也有一些定时器的线程池,等等。

一、线程池是什么

线程池就是一个对象持有一堆线程,举个例子就是饿了么养的骑手团队。线程池就是这个团队,每个骑手都是一个线程。

二、为什么要用线程池?

假如现在商家有外卖单子,需要骑手去送单,这个时候的外卖任务就会派单给骑手,为什么要用线程池呐?

有几个好处,第一就是骑手的招聘是有成本的,等你有了外卖订单再去招聘,来不及了,不如平常养一些骑手,线程的创建和销毁的开销是巨大的。

第二就是不能一个单子来了就来一个骑手,这样的话骑手的数量很难控制,对于派单来说也存在很大的压力,会造成整个骑手团队的崩溃,对应的就是可以通过线程池控制系统内的线程数量,有效的避免大量的线程池争夺CPU资源而造成堵塞。

第三如果养了一个骑手团队,这样在骑手的管理上可以规范,以便提供更好的外卖服务,比如这种外卖超时,骑手打星等。对比线程池就是线程池可以提供定时、定期、单线程、并发数控制等功能。

三、看下类图,从整体上理解下

image.png

四、线程池的创建

image.png

线程池主要使用的四种

固定数量的线程池(FixedThreadPool)

定时线程池(ScheduledThreadPool )

可缓存线程池(CachedThreadPool)

单线程化线程池(SingleThreadExecutor)

五、线程池核心参数说明

首先看下如何构造一个线程池

public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
   return new ThreadPoolExecutor(nThreads, nThreads,
                                 0L, TimeUnit.MILLISECONDS,
                                 new LinkedBlockingQueue<Runnable>(),
                                 threadFactory);
                                  
   public ThreadPoolExecutor(int corePoolSize,
                         int maximumPoolSize,
                         long keepAliveTime,
                         TimeUnit unit,
                         BlockingQueue<Runnable> workQueue,
                         ThreadFactory threadFactory,
                         RejectedExecutionHandler handler)

核心参数说明:

image.png

六、几个疑问点

6.1、是怎么保证线程不销毁的?

核心线程会阻塞等待workQueue

6.2 提交任务有哪几种方式?

image.png

6.3 拒绝策略都有哪些?

拒绝策略(handler)当线程池的线程数达到最大线程数时,需要执行拒绝策略。拒绝策略需要实现RejectedExecutionHandler接口,并实现rejectedExecution(Runnable r, ThreadPoolExecutor executor)方法。不过Executors框架已经为我们实现了4种拒绝策略:

AbortPolicy(默认):丢弃任务并抛出RejectedExecutionException异常。

CallerRunsPolicy:由调用线程处理该任务。

DiscardPolicy:丢弃任务,但是不抛出异常。可以配合这种模式进行自定义的处理方式。

DiscardOldestPolicy:丢弃队列最早的未处理任务,然后重新尝试执行任务。

6.4 线程池的关闭

关闭线程池可以调用shutdownNow和shutdown两个方法来实现

shutdownNow:对正在执行的任务全部发出interrupt(),停止执行,对还未开始执行的任务全部取消,并且返回还没开始的任务列表。

shutdown:当我们调用shutdown后,线程池将不再接受新的任务,但也不会去强制终止已经提交或者正在执行中的任务。

6.5 初始化线程池时线程数的选择

如果任务是IO密集型,一般线程数需要设置2倍CPU数以上,以此来尽量利用CPU资源。

如果任务是CPU密集型,一般线程数量只需要设置CPU数加1即可,更多的线程数也只能增加上下文切换,不能增加CPU利用率。

具体问题具体分析。

七、总结

线程池是项目中常用的,需要理解线程池的应用场景和构造函数,正确的使用线程池。

打字不容易,点赞,转发,关注三连,谢谢大家