前言
ThreadPoolExecutor 是 JDK1.5 之后才有的线程池类,JDK 帮咱们实现了基于 ThreadPoolExecutor 创立的 newSingleThreadExecutor、newFixedThreadPool、newCachedThreadPool 等方便使用的线程池,那么为什么这些线程池在阿里巴巴的开发标准中却不举荐应用呢?我置信读了这篇文章后你将恍然大悟。
提醒:以下是本篇文章正文内容,上面案例可供参考
一、技术介绍
1. 线程池是什么?
线程池是一种多线程解决模式,解决过程中将工作增加到队列,而后在创立线程后主动启动这些工作。线程池线程都是后盾线程。每个线程都应用默认的堆栈大小,以默认的优先级运行,并处于多线程单元中。如果某个线程在托管代码中闲暇(如正在期待某个事件), 则线程池将插入另一个辅助线程来使所有处理器放弃忙碌。如果所有线程池线程都始终保持忙碌,但队列中蕴含挂起的工作,则线程池将在一段时间后创立另一个辅助线程但线程的数目永远不会超过最大值。超过最大值的线程能够排队,但他们要等到其余线程实现后才启动。— 摘自百度百科
二、应用步骤
1.ThreadPoolExecutor 参数介绍
咱们看下 ThreadPoolExecutor 类的 execute 办法底层源码进行剖析
OK,依据判断可知:
1. 如果正在运行的线程少于 corePoolSize 线程,请尝试应用给定命令作为其第一个工作启动一个新线程。
2. 如果工作能够胜利排队,那么咱们依然须要再次查看是否应该增加线程(因为现有线程自上次查看后就死掉了),或者自进入此办法后该池已敞开。因而,咱们从新查看状态,并在必要时回滚排队,如果进行,或者如果没有线程,则启动一个新线程。
3. 如果咱们无奈将工作排队,则尝试增加一个新线程。如果失败,咱们晓得咱们已敞开或处于饱和状态,因而回绝该工作。
2.newSingleThreadExecutor 应用
代码如下(示例):
@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 的源码
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,期待队列为无限大的线程池,所以你应该晓得为什么它只开了一个线程去执行了。
3.newFixedThreadPool 应用
代码如下(示例):
@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 是一个依据传入参数来执行固定大小的线程池
4.newCachedThreadPool 应用
代码如下(示例):
@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 它是一个能够有限扩充的线程池,以后没有闲暇线程时它会创立一个新的线程,如果有闲暇线程会应用闲暇线程解决
5. 线程池的应用举荐
通过以上的测试案例与源码剖析,置信大家对线程池有了肯定的意识,总结如下:
1.newSingleThreadExecutor:只开启一个线程运行,解决效率较慢,阻塞队列大小是没有大小限度的,如果队列沉积数据太多会造成资源耗费
2.newFixedThreadPool:一个固定大小的线程池,可控制线程并发数量,但阻塞队列大小是没有大小限度的,如果队列沉积数据太多会造成资源耗费
3.newCachedThreadPool:比拟适宜解决执行工夫较短的业务,但线程若是无限度的创立,可能会导致内存占用过多而产生 OOM,并且会造成 cpu 适度切换耗费太多资源。
所以应用举荐是依据业务场景实现自定义 ThreadPoolExecutor,特地是高并发大流量零碎,这也是为什么阿里外部不举荐应用以上几种线程池的起因。
参考:《2020 最新 Java 根底精讲视频教程和学习路线!》
链接:https://juejin.cn/post/692273…