线程资源必须通过线程池提供,不应该在线程中自行显示的创建线程。
线程池能减少在创建和销毁线程上所消耗的时间以及系统资源的开销,解决资源不足的问题。
如果不使用线程池,有可能造成系统创建大量同类线程而导致内存被消耗完或者“CPU过度切换”的问题。
线程池尽量不要使用Executors去创建,而应使用ThreadPoolExecutors的方式创建自定义的线程池。
Executors创建出来的线程池都实现了ExecutorService接口。常用方法有以下几个:
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue());
}
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue());
}
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue()));
}
newScheduledThreadPool(int corePoolSize)
创建一个支持定时及周期性的任务执行的线程池,用来处理延时任务或定时任务,多数情况下可用来替代Timer类。
newWorkStealingPool()
public static ExecutorService newWorkStealingPool() {
return new ForkJoinPool
(Runtime.getRuntime().availableProcessors(),
ForkJoinPool.defaultForkJoinWorkerThreadFactory,
null, true);
}
Java中的BlockingQueue主要有两种实现,分别是ArrayBlockingQueue 和 LinkedBlockingQueue。
ArrayBlockingQueue是一个用数组实现的有界阻塞队列,必须设置容量。
LinkedBlockingQueue是一个用链表实现的有界阻塞队列,容量可以选择进行设置,不设置的话,将是一个无边界的阻塞队列,最大长度为Integer.MAX_VALUE。
而newFixedThreadPool和newSingleThreadExecutor中创建LinkedBlockingQueue时,并未指定容量。此时,LinkedBlockingQueue就是一个无边界队列,对于一个无边界队列来说,是可以不断的向队列中加入任务的,这种情况下就有可能因为任务过多而导致内存溢出问题。
而newCachedThreadPool和newScheduledThreadPool这两个方法就安全了,这两种方式创建的最大线程数可能是Integer.MAX_VALUE,而创建这么多线程,必然就有可能导致OOM。
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
参数说明:
| 名称 | 类型 | 含义 |
|---|---|---|
| corePoolSize | int | 核心线程数数 |
| maximumPoolSize | int | 最大线程池数 |
| keepAliveTime | long | 线程最大空闲时间 |
| unit | TimeUnit | 时间单位 |
| workQueue | BlockingQueue | 线程等待队列 |
| threadFactory | ThreadFactory | 线程创建工厂 |
| handler | RejectedExecutionHandler | 拒接策略 |
| 拒绝策略 | 拒绝行为 |
|---|---|
| AbortPolicy | 抛出RejectedExecutionException |
| DiscardPolicy | 什么也不做,直接忽略 |
| DiscardOldestPolicy | 丢弃执行队列中最老的任务,尝试为当前提交的任务腾出位置 |
| CallerRunsPolicy | 直接由提交任务者执行这个任务 |

CPU 密集型任务,理论上 线程数量 = CPU 核数(逻辑) 就可以了,但是实际上,数量一般会设置为 CPU 核数(逻辑)+ 1
I/O 密集型任务,理论上
最佳线程数 = CPU核心数 * (1/CPU利用率) = CPU核心数 * (1 + (I/O耗时/CPU耗时))
但是实际上,线程数量一般会设置为 2 x CPU 核数(逻辑)+ 1
参考 https://mp.weixin.qq.com/s/0WzUU-4r164jY7M5NTmEYg