提示:若有疑问请私信联系、下方有源代码地址,请自行拿取git
提示:如下是本篇文章正文内容,下面案例可供参考markdown
参数名称 | 参数类型 | 参数含义 |
---|---|---|
corePoolSize | int | 核心线程池大小 |
maximumPoolSize | int | 最大线程池大小 |
keepAliveTime | long | 线程最大空闲时间 |
unit | TimeUnit | 时间单位 |
workQueue | BlockingQueue | 线程等待队列 |
threadFactory | ThreadFactory | 线程建立工厂 |
handler | RejectedExecutionHandler | 拒绝策略 |
咱们看下ThreadPoolExecutor类的execute方法底层源码进行分析 | ||
![]() |
||
OK,根据判断可知: |
1.若是正在运行的线程少于corePoolSize线程,请尝试使用给定命令做为其第一个任务启动一个新线程。多线程
2.若是任务能够成功排队,那么咱们仍然须要再次检查是否应该添加线程(由于现有线程自上次检查后就死掉了),或者自进入此方法后该池已关闭。所以,咱们从新检查状态,并在必要时回滚排队,若是中止,或者若是没有线程,则启动一个新线程。并发
3.若是咱们没法将任务排队,则尝试添加一个新线程。若是失败,咱们知道咱们已关闭或处于饱和状态,所以拒绝该任务。函数
代码以下(示例):高并发
@Test public void testNewSingleThreadExecutor() {
ExecutorService threaPool = Executors.newSingleThreadExecutor();
long start = System.currentTimeMillis();
System.out.println("线程池执行开始");
int idx = 10;
while (--idx > 0) {
threaPool.execute(() -> {
try {
LOGGER.info("线程执行中");
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException interruptedException) {
interruptedException.printStackTrace();
}
});
}
threaPool.shutdown();
for (; ; ) {
if (threaPool.isTerminated())
break;
}
long end = System.currentTimeMillis();
System.out.println("线程池执行结束,总用时:" + (end - start) + " ms ");
}
复制代码
此测试方法运行的结果以下: 注意看我用红框标记的地方,只采用了1个线程去执行,原理是什么呢?让咱们看看newSingleThreadExecutor的源码oop
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
复制代码
构建了ThreadPoolExecutor线程池,核心线程1个,最大执行线程1个,等待队列是LinkedBlockingQueue,我们再点进去看看LinkedBlockingQueue默认构造函数是啥
能够看到这是默认时一个容量为Interger.MAX_VALUE的队列源码分析
结论:newSingleThreadExecutor是一个核心线程为1,线程池中容许最大线程为1,等待队列为无限大的线程池,因此你应该知道为何它只开了一个线程去执行了。单元测试
代码以下(示例):测试
@Test public void testNewFixedThreadPool() {
ExecutorService threaPool = Executors.newFixedThreadPool(5);
long start = System.currentTimeMillis();
System.out.println("线程池执行开始");
int idx = 20;
while (--idx >= 0) {
threaPool.execute(() -> {
try {
LOGGER.info("线程执行中");
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException interruptedException) {
interruptedException.printStackTrace();
}
});
}
threaPool.shutdown();
for (; ; ) {
if (threaPool.isTerminated())
break;
}
long end = System.currentTimeMillis();
System.out.println("线程池执行结束,总用时:" + (end - start) + " ms ");
}
复制代码
先来看下执行结果
OK,看下执行结果可知,只开启了5个线程,每次批量的执行5个,接下来我们看看它的源码
也一样的构造了ThreadPoolExecutor线程池,参数为:核心线程数、线程池最大线程数都为传入的参数,单元测试传的是5,因此开5个线程运行,运行完重复使用这5个线程去执行队列中的。
结论:newFixedThreadPool是一个根据传入参数来执行固定大小的线程池
代码以下(示例):
@Test public void testNewCachedThreadPool() {
ExecutorService threaPool = Executors.newCachedThreadPool();
long start = System.currentTimeMillis();
System.out.println("线程池执行开始");
int idx = 200;
while (--idx >= 0) {
threaPool.execute(() -> {
LOGGER.info("线程执行中");
});
}
threaPool.shutdown();
for (; ; ) {
if (threaPool.isTerminated())
break;
}
long end = System.currentTimeMillis();
System.out.println("线程池执行结束,总用时:" + (end - start) + " ms ");
}
复制代码
OK,这里跟上面不一样,我们执行200个线程,咋们先看执行结果, 很明显能够看到跟上面的不一样,在执行时间很短的任务时重复的利用线程去执行,缘由是什么呢?我们先看源码
建立了一个核心线程数为0,最大执行线程为Interger.MAX_VALUE,而且注意这里用了SynchronousQueue这个队列,SynchronousQueue没有容量,是无缓冲等待队列,是一个不存储元素的阻塞队列,会直接将任务交给消费者,必须等队列中的添加元素被消费后才能继续添加新的元素。
SynchronousQueue,至于它的底层原理后期会写一篇专门关于队列的文章,这里再也不细说
结论:newCachedThreadPool它是一个能够无限扩大的线程池,当前没有空闲线程时它会建立一个新的线程,若是有空闲线程会使用空闲线程处理
经过以上的测试案例与源码分析,相信你们对线程池有了必定的认识,总结以下:
1.newSingleThreadExecutor:只开启一个线程运行,处理效率较慢,阻塞队列大小是没有大小限制的,若是队列堆积数据太多会形成资源消耗
2.newFixedThreadPool:一个固定大小的线程池,可控制线程并发数量,但阻塞队列大小是没有大小限制的,若是队列堆积数据太多会形成资源消耗
3.newCachedThreadPool:比较适合处理执行时间较短的业务,但线程如果无限制的建立,可能会致使内存占用过多而产生OOM,而且会形成cpu过分切换消耗太多资源。
因此使用推荐是根据业务场景实现自定义ThreadPoolExecutor,特别是高并发大流量系统,这也是为何阿里内部不推荐使用以上几种线程池的缘由。
是否是感受很简单?更多用法请点击下方查看源码,关注我带你揭秘更多高级用法
源码地址:点此查看源码.