点关注,不迷路;持续更新Java相关技术及资讯!!!常见线程池
线程池参数的意义及工做原理java
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
//...
}
复制代码
线程池有这么几个重要的参数缓存
corePoolSize: 线程池里的核心线程数量bash
maximumPoolSize: 线程池里容许有的最大线程数量分布式
keepAliveTime: 若是 当前线程数量 > corePoolSize,多出来的线程会在keepAliveTime以后就被释放掉源码分析
unit: keepAliveTime的时间单位,好比分钟,小时等学习
workQueue: 队列ui
threadFactory: 每当须要建立新的线程放入线程池的时候,就是经过这个线程工厂来建立的url
handler: 就是说当线程,队列都满了,以后采起的策略,好比抛出异常等策略spa
那么咱们假设来一组参数练习一下这个参数的意义线程
corePoolSize:1
mamximumPoolSize:3
keepAliveTime:60s
workQueue:ArrayBlockingQueue,有界阻塞队列,队列大小是4
handler:默认的策略,抛出来一个ThreadPoolRejectException复制代码
1.一开始有一个线程变量poolSize维护当前线程数量.此时poolSize=0
2.此时来了一个任务.须要建立线程.poolSize(0) < corePoolSize(1),那么直接建立线程
3.此时来了一个任务.须要建立线程.poolSize(1) >= corePoolSize(1),此时队列没满,那么就丢到队列中去
4.若是队列也满了,可是poolSize < mamximumPoolSize,那么继续建立线程
5.若是poolSize == maximumPoolSize,那么此时再提交一个一个任务,就要执行handler,默认就是抛出异常
6.此时线程池有3个线程(poolSize == maximumPoolSize(3)),假如都处于空闲状态,可是corePoolSize=1,那么就有(3-1 =2),那么这超出的2个空闲线程,空闲超过60s,就会给回收掉.
以上,就是线程池参数意义及工做原理
线程池参数设计上的思考
知道了以上的原理,那么咱们看看常见的两个线程池FixedThreadExecutor和CachedThreadExecutor的参数设计
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
复制代码
那么问题来了
1.为何FixedThreadExecutor的corePoolSize和mamximumPoolSize要设计成同样的?
2.为何CachedThreadExecutor的mamximumPoolSize要设计成接近无限大的?
敲黑板划重点
仍是前面那句话,咱们看源码,并非大段大段的源码打上注释,最重要的是通过深度思考,明白做者设计的意图,这也就是为何市场上有这么多源码解析文章,咱们依然还要关注一下肥朝(卖个萌)
若是你对上面的线程池的原理,参数有了清晰的认识,天然很快就能明白这个设计思路.
好比问题一,由于线程池是先判断corePoolSize,再判断workQueue,最后判断mamximumPoolSize,然而LinkedBlockingQueue是无界队列,因此他是达不到判断mamximumPoolSize这一步的,因此mamximumPoolSize成多少,并无多大所谓
好比问题二:咱们来看看SynchronousQueue的注释:
从我圈的这几个小学英文单词都知道,这个队列的容量是很小的,若是mamximumPoolSize不设计得很大,那么就很容易动不动就抛出异常
线程池使用上的建议
原理明白了,设计思想咱们也明白了,代码要怎么写.光理论还不行,也就是说,咱们在项目中,线程池究竟要怎么用?那么咱们来看一下阿里手册,看到这个强制相信不用我多说什么
Dubbo线程池
那么咱们来看看Dubbo官方文档,一直强调,官方文档才是最好的学习资料.
回归问题
那么回到咱们前面遇到的问题.咱们看了官方文档说Dubbo默认(缺省)用线程池是fixed,那么咱们第一反应,从前面各类分析原理也得知了,FixedThreadPool的队列是很大的,他根本达不到第三个判断条件mamximumPoolSize,达不到第三个条件,也就不会触发handle抛出异常.那前面那个压测问题的异常怎么来的。
直入源码
这种问题.搜索是很差使了,由于根本很差搜索.那么咱们只好直入源码了
@SPI("fixed")
public interface ThreadPool {
/**
* 线程池
*
* @param url 线程参数
* @return 线程池
*/
@Adaptive({Constants.THREADPOOL_KEY})
Executor getExecutor(URL url);
}
public class FixedThreadPool implements ThreadPool {
public Executor getExecutor(URL url) {
String name = url.getParameter(Constants.THREAD_NAME_KEY, Constants.DEFAULT_THREAD_NAME);
int threads = url.getParameter(Constants.THREADS_KEY, Constants.DEFAULT_THREADS);
int queues = url.getParameter(Constants.QUEUES_KEY, Constants.DEFAULT_QUEUES);
return new ThreadPoolExecutor(threads, threads, 0, TimeUnit.MILLISECONDS,
queues == 0 ? new SynchronousQueue<Runnable>() :
(queues < 0 ? new LinkedBlockingQueue<Runnable>()
: new LinkedBlockingQueue<Runnable>(queues)),
new NamedThreadFactory(name, true), new AbortPolicyWithReport(name, url));
}
}
复制代码
此时咱们发现,Dubbo里面的FixedThreadPool和newFixedThreadPool建立出来的FixedThreadPool参数是不同的.默认状况下,Dubbo的FixedThreadPool中,maximumPoolSize = 200,队列是容量很小的SynchronousQueue.因此当线程超过200的时候,就会抛出异常.这个和咱们上面分析的原理是一致的.
其实换个角度想,规范手册都是阿里出的,阿里手册都强制说要用ThreadPoolExecutor的方式来建立了,并且还给你分析了无界队列的风险,那么Dubbo官方文档说的fixed又怎么会是Executors建立的无界队列这种呢?
知道了线程池的原理和异常的根源以后,咱们彻底能够根据业务特色的不一样,自定义线程池参数,来避免这类异常的频繁发生.亦或者改变Dubbo默认的线程模型,从aLL改为message等,这个就要结合实际的业务状况了。
关注、转发、评论天天分享java 知识,赠送Spring源码分析、Dubbo、Redis、Netty、zookeeper、Spring cloud、分布式资料