问题来源:阿里巴巴开发手册并发编程这块写有一条:线程池不允许使用Executors去创建,而是通过ThreadPoolExecutor的方式创建.
线程池的优点:
减少内存资源的消耗.
提高请求处理速度
避免出现OOM
Executors创建线程的方式有三种.只讨论下面这种:
Executors创建 ThreadPoolExecutor对象的方法有三个:
Executors.newCachedThreadPool (创建可缓存的线程池)
Executors.newSingleThreadExecutor (创建单线程的线程池)
Executors.newFixedThreadPool(创建固定长度的线程池)
ThreadPoolExecutor构造函数有四个最终调用的都是一个:
//手动创建一个线程池
ThreadPoolExecutor thread1 =
new ThreadPoolExecutor(corePoolSize, //核心线程数
maximumPoolSize, //最大池的大小
keepAliveTime, //空闲线程保持时间
unit, //时间单位
workQueue,//阻塞队列
handler); //拒绝服务助手 回调函数,给出提示信息
ThreadPoolExecutor threadPool=
new ThreadPoolExecutor(5, //核心线程数
10, //最大池的大小
30L,//空闲线程保持时间30
TimeUnit.SECONDS,//时间单位秒
new ArrayBlockingQueue(5),//阻塞线程
new RejectedExecutionHandler() {//拒绝服务助手 回调函数,给出提示信息,多态,内部类,回调函数
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
System.out.println("线程数超过了线程池的容量,拒绝执行任务-->"+r);
}
});
手动创建一个线程池,用到的是ThreadPoolExecutor类的六个参数的构造方法
以上面的方法为例:
我们是设置核心线程池数5个,最大线程池10个,线程空闲时间30秒,长度为5的阻塞队列,和拒绝服务助手运行时
运行时,首先用核心线程池来调度线程有新的任务,先用核心线程来处理.
第一种情况,核心线程数没有达到5个,那么有任务进来就会创建新的线程,直到有5个核心线程
第二种情况,已经有5个核心线程,这时候有任务来,先看有没有空闲的核心线程,有的话就用核心线程,没有就把它放进队列中
等待有空闲的核心线程,再把任务从队列中take出来,给空闲核心线程
在队列中,队列为空和队列满都是阻塞状态
第三种情况,核心线程已经满了,队列中也满了,就会创建临时线程,来处理任务,运行一段时间后,
这时候如果有核心线程或是临时线程空闲,线程池就会对这些线程,看其是否达到了线程空闲时间
达到了线程空闲时间的线程,就会被销毁,他会把没执行完的临时线程变为核心线程,
执行完的核心线程和临时线程被销毁,为了保持核心线程数是5
第四种情况,核心线程已经满了,队列中也满了,临时线程也满了,就会调用拒绝服务助手,提示报错.
注意:
ThreadPoolExecutor 会根据核心线程池数和最大大小,来自动调整
当我们用execute(Runnable)提交任务小于核心线程数时就是第一种情况,优先创建新线程直到等于核心线程数
当我们核心线程没有空闲,队列也满了,这时就会创建临时线程,但临时线程数+核心线程数不能超过最大大小
如果核心线程数和最大大小设置相等,则是创建了固定大小的线程池,
如果最大大小设置特别大(像Integer.MAX_VALUE),这表示没有上限,(但也要根据CPU,很为这很吃CPU)
一般时候,我们根据构造来设置核心线程池和最大大小,但也可以调用方法来设置.
Executors.newCachedThreadPool (创建可缓存的线程池)
其中的核心线程数为0 ,队列是一个不存储元素的队列,
因为Integer.MAX_VALUE非常大,所以,就会无限创建非核心线程,
造成OOM.
Executors.newSingleThreadExecutor (创建单线程的线程池)
这个会创建一个核心线程,然后其他的放到队列中,
LinkedBlockingQueue队列的大小是Integer.MAX_VALUE非常大.就可以往里面无限放任务,
所以在资源有限的情况下,也容易造成OOM.
Executors.newFixedThreadPool(创建固定长度的线程池)
和上面的类似LinkedBlockingQueue队列非常大,容易造成OOM