aglw100   发表于 2020-2-13 21:01:28 |栏目:






首先了解下Executors的方法创建出来的5种常用线程池




名称
说明




newCachedThreadPool
可缓存的线程池


newFixedThreadPool
固定大小的线程池


newSingleThreadExecutor
固定单个线程的线程池


newScheduledThreadPool
用作任务调度的线程池


newWorkStealingPool
足够大小的线程池,JDK1.8新增的



看下每种线程池都是怎么创建的


newCachedThreadPool

public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
12345
可以看到它允许创建的最大线程数是Integer.MAX_VALUE,使用的任务队列是SynchronousQueue,是个用于线程同步的阻塞队列,它没有实际的容量。所以如果直接创建这个线程池,很可能会创建大量线程,导致OOM。

newFixedThreadPool

public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
12345
可以看到它的核心线程数和最大线程数是一样的,由使用者来决定具体值,使用的任务队列是LinkedBlockingQueue,是个有界阻塞队列,如下源码,可知这个队列的最大长度是Integer.MAX_VALUE,这样可能会导致大量的请求堆积,直接OOM。
public LinkedBlockingQueue() {
this(Integer.MAX_VALUE);
}
123

newSingleThreadExecutor

public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
123456
可以看到它的核心线程数和最大线程数是一样的,固定是1,使用的任务队列是LinkedBlockingQueue,是个有界阻塞队列,这个队列的最大长度是Integer.MAX_VALUE,这样可能会导致大量的请求堆积在这个队列中,导致出现OOM。

newScheduledThreadPool

public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue());
}
12345678
可以看到它允许创建的最大线程数是Integer.MAX_VALUE,使用的队列是DelayedWorkQueue,是个支持延迟操作的无界阻塞队列。所以使用这个线程池,如果有大量线程被创建,就会导致OOM。

newWorkStealingPool

public static ExecutorService newWorkStealingPool(int parallelism) {
return new ForkJoinPool
(parallelism,
             ForkJoinPool.defaultForkJoinWorkerThreadFactory,
             null, true);
}
public ForkJoinPool(int parallelism,
                        ForkJoinWorkerThreadFactory factory,
                        UncaughtExceptionHandler handler,
boolean asyncMode) {
this(checkParallelism(parallelism),
checkFactory(factory),
             handler,
             asyncMode ? FIFO_QUEUE : LIFO_QUEUE,
"ForkJoinPool-" + nextPoolId() + "-worker-");
checkPermission();
}
123456789101112131415161718
这个线程池它的实现和上面4种都不一样,用的是ForkJoinPool类。这是个并行的线程池,传入的参数是并发线程数,它里面的任务执行是无序的,哪个线程抢到任务就哪个线程执行,ForkJoinPool主要用到的是双端队列。

总结

从以上对各个类型的线程池的分析可以发现,除了newWorkStealingPool用的是ForkJoinPool类,其他线程池的构建最后其实都调用了ThreadPoolExecutor类的构造方法去创建线程池。而这些类型的线程池只是根据自己的需要来传入了一些默认的参数,也正是因为这些参数,才会导致可能出现OOM的问题。那么看下使用ThreadPoolExecutor来创建线程池的示例代码:
ExecutorService executorService = new ThreadPoolExecutor(1, 1, 60, TimeUnit.SECONDS, new LinkedBlockingQueue(1));
1
可以看到使用ThreadPoolExecutor来创建线程池,里面所有的参数我们都可以根据需求来自己指定,这样使用起来就放心了许多。所以推荐使用ThreadPoolExecutor来创建线程池。

回复 显示全部楼层 使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

作者相关信息

更多资源

精品推荐

极品资源

原创模板

下载排行

热门标签

快速回复 返回顶部 返回列表