为什么要使用线程池
1 线程复用 控制最大并发数 管理线程
2 降低消耗:可以直接从线程 中取出线程,避免创建新线程时的消耗
3 提高响应速度:当任务到达时,不需要等待线程的创建
4 提高线程的可管理性:如果随意创建多个线程,会浪费系统资源。使用线程池可以统一分配管理。
线程池的运行流程
①如果在线程池中的线程数量没有达到核心的线程数量,这时候就会启动一个核心线程来执行任务。(即优先使用核心线程)。
②如果线程池中的线程数量已经超过核心线程数,这时候任务就会被插入到任务队列中排队等待执行。
③由于任务队列已满,无法将任务插入到任务队列中。这个时候如果线程池中的线程数量没有达到线程池所设定的最大值,那么这时候就会立即启动一个非核心线程来执行任务。
④如果线程池中的数量达到了所规定的最大值,那么就会拒绝执行此任务,这时候就会调用RejectedExecutionHandler中的rejectedExecution方法来通知调用者。
线程池的三种实例
线程池的底层是就ThreadPoolExecutor
1 newFixedThreadPool
ExecutorService pool = Executors.newFixedThreadPool(3)
创建一个线程数量固定的线程池
适用于长期执行任务
底层使用阻塞队列 LinkedBlockingQueue
只有核心线程
2 newSingleThreadExecutor
创建只有一个线程的线程池
适合任务是一个接一个地来
3 Executors.newCachedThreadPool();
创建一个可以动态扩容的线程池
适合执行很多短期异步的任务
全部为非核心线程
4
4 线程池的使用实例
class MyCallable implements Callable<String>{
@Override
public String call() throws Exception {
String info = Thread.currentThread().getName() + " come in call get " + UUID.randomUUID().toString().substring(0, 6);
// System.out.println(info);
return info;
}
}
public class ThreadPoolTest {
public static void test() {
ExecutorService pool = Executors.newFixedThreadPool(3);
// ExecutorService pool = Executors.newSingleThreadExecutor();
// ExecutorService pool = Executors.newCachedThreadPool();
try {
for (int i = 0; i < 10; i++) {
FutureTask<String> ft = new FutureTask<>(new MyCallable());
pool.submit(ft);
while(!ft.isDone()){}
System.out.println(ft.get());
}
} catch (Exception ex) {
ex.printStackTrace();
} finally {
pool.shutdown();
}
}
线程池的七大参数
我们在实例化一个线程池时,底层调用是TrheadPoolExecutor
1 核心线程数 corePoolSize :线程池中长驻的线程数量。该类型线程不会被销毁
2 线程最大值 maximumPoolSize:线程池中能容纳的线程数。包括核心和非核心的
3 阻塞队列 workQueue:任务队列。存放已提交但还未执行的任务
4 keepAliveTime:非核心线程可以空闲的时间
5 unit: keepali vetime的单位
6 threadFactory: 生成线程的工厂,一般为默认值
7 拒绝策略 rejectedExecutionHandler:当核心线程已经使用 等待队列已满且线程数量已到达最大时,开启拒绝策略
四个拒绝策略
1 AbortPolicy:直接抛出RejectedExecutionExecption异常阻止系统正常运行
2 CallerRunsPolicy:既不会放弃任务,也不会抛出异常,而是将任务回退到调用者,即让调用者执行这个任务。
3 DiscardOldertPolicy: 抛出队列中等待最久的任务,然后把新任务添加到队列中
4 DiscardPolicy: 直接抛弃任务。但不抛出异常。最好的方法
线程池使用上的优化
1 使用自定义的LinkedBockingQueue
因为默认的线程池底层使用的阻塞队列为LinkedBlockingQueue,该队列的可容纳的元素太多,会造成OOM(内存用完)。
通过ThreadPoolExecutor来自定义线程池 在实例化LinkedBlockingQueue时,设置该队列的容量,并设置拒绝策略为直接抛弃。
ExecutorService threadPool = new ThreadPoolExecutor(2, 5, 3L,
TimeUnit.SECONDS, new LinkedBlockingQueue<>(3), Executors.defaultThreadFactory(),
new ThreadPoolExecutor.DiscardPolicy());
2 线程池中线程数量的最大值
1 CPU密集型:CPU一直全速运行。此时应设置线程数较少,为CPU核心数+·1.
2 IO密集型:可以多分配一点线程数量
CPU核心数 * 2
CPU核心数 / (1 - 阻塞系数) 阻塞系数为0.8~0.9
获取CPU核心数的方式
Runtime.getRuntime().availableProcessors()