线程池是我们在进行开发中常用的一种工具,其中Executors可能是我们接触到一种比较早的线程池操作工具类,但是在实际开发中是阿里巴巴开发规范强烈不建议使用该工具类,我以前在用线程池的时候就大量使用了Executors工具类,结果通过工具扫描一片报红,在仔细看了一些博客和自己看了一下相关源码,特记录下自己的理解。
谈到线程池,我第一个使用到的是Executors这个工具类(主要是copy大法好啊,当时也不懂,看到别人这么使就照抄了,现在给自己留下一堆坑啊。。。)
Executors有四个常用的初始化线程池的方法,分别是
先说一下这几个方法的缺点:fixedThreadPool和singleThreadExecutor对于排队的队列没有数量限制,最大支持Integer.MAX_VALUE个;cachedThreadPool和scheduledThreadPool中最大线程数可以达到Integer.MAX_VALUE个;当线程过多的时候,这些方法就容易造成OOM了,这里没有基础的同学(比如我)可能会有点儿迷惑,为什么会有这些缺点呢?因为Executors也是依赖ThreadPoolExecutor实现的,所以我们先来看一下ThreadPoolExecutor的使用,再回头来分析上面的缺点就好理解了。
ThreadPoolExecutor有多个构造器,我们就看这一个最全的就好了,
我们可以通过代码演示这几个参数的用法
自定义的拒绝策略:只打印被拒绝的线程信息和当前线程池的情况,一般使用默认或者定义好的拒绝策略就可以,这里为了演示使用了自己实现
自定义的线程工厂,一般情况下用内部线程工厂就行,没必要自己实现,除非有自己的特殊需要
业务线程类
main方法测试
这里我们的核心线程数是2,最大线程数是4,阻塞队列长度是2;所以理论上同时处理量是4个业务线程,2个业务线程进入堵塞队列中,等待前面4个线程执行完毕,再去处理,由于我们的并发线程数是10所有必然有4个线程会被拒绝,并打印我们的写的日志。
以下是运行结果:
这也基本上印证了我们的判断,所以我们可以这么理解这些参数:线程池会初始化创建核心线程池个数的线程,当有超过核心线程数的线程提交的后,会放入阻塞队列,如果阻塞队列满了,则扩大到最大线程数个数,当继续有线程提交,此时队列已满,无法处理,就会按照设定的策略拒绝这些线程。当当前任务小于最大线程数的时候,线程资源会保持核心线程池个数的线程,其他超过的线程资源在存活时间时间之后会被回收。
这里我们应该会对ThreadPoolExecutor有了一定的理解,那么再回头一下Executors中相关代码
这里我们可以看到,newFixedThreadPool和newSingleThreadExecutor中阻塞对列没有数量限制,默认是Integer.MAX_VALUE,这样在高并发下特别容易导致阻塞对列多大,造成OOM;
newCachedThreadPool和scheduledThreadPool的最大线程数都是Integer.MAX_VALUE,这样在高并发下特别容易导致创建过多线程,造成OOM。