为什么使用线程池
使用线程池可以复用线程、管理资源,避免频繁创建、销毁线程带来的性能开销。
- 线程创建、开销对系统资源开销较大。
- 使用线程池,可以预先创建一定数量的线程,放入线程池,需要执行任务可以从线程池中获取复用线程。
线程池参数(ThreadPoolExecutor)
-
corePoolSize核心线程数量线程池中长久存活的线程
-
maximumPoolSize最大线程数量线程池中最大线程数量,临时线程用完销毁
-
keepAliveTime临时线程最大存活时间 -
unit临时线程最大存活时间单位 -
workQueue任务阻塞队列提交任务数超出核心线程数之后,再提交的任务就存放在工作队列
-
threadFactory线程工厂 创建线程的工厂类 -
handler任务拒绝策略
线程池参数设置
线程数量不是越多越好,频繁的线程上下文交换会影响系统性能。
Runtime.getRuntime().avilableProcessors()获取CPU最大核心数
- CPU密集型:任务需要大量计算,很少阻塞,cpu一直处于忙碌状态 无法切换线程上下文。CPU核数 + 1。
- I/O密集型:任务涉及大量IO操作,在IO阻塞等待时,cpu可以切换线程上下文处理其他任务。2 * CPU核数。
线程池提交任务流程
- 提交任务,线程数小于corePoolSize 创建核心线程执行任务。
- 任务数量超过corePoolSize,后续任务就进入阻塞队列等待核心线程获取执行。
- 阻塞队列也满了,后续创建 maximumPoolSize - corePoolSize个临时线程来执行任务。任务执行完成,临时线程等待keepAliveTime之后自动销毁。
- 任务达到maximumPoolSize,阻塞队列还是满的,根据不同的拒绝策略处理。
- AbortPolicy 抛出RejectedExecutionException,新任务立即拒绝,不会加入阻塞队列,也不会执行,适合运行部分任务失败的场景。
- DiscardPolicy 丢弃新任务不抛出异常,适合允许丢失少量任务的场景。
- DiscardOldestPolicy 将阻塞队列最早的任务删除,强调新任务更重要 强调实时性。
- CallerRunsPolicy 将任务回退给调用线程,不抛出异常,适合任务不丢失 接受任务延迟的场景。
Executor创建线程池存在问题
Executor是一个工厂类,提供了创建不同线程池的静态方法:newCachedThreadPool、newFixedThreadPool、newSingleThreadPoolExecutor、newScheduledThreadPool。
Executor使用起来比较方便,但是可能存在一些问题:
- 固定线程数量和单个线程的线程池,线程数量不会溢出,但是任务阻塞队列可能溢出,可能堆积大量任务,导致OOM。
newCachedThreadPool、newScheduledThreadPool线程数量可能溢出,创建大量线程,导致OOM。
线程池不要使用Executor创建,而是通过ThreadPoolExecutor创建,规避资源耗尽的风险。
自定义线程池
public class MyThreadPool {
private fianl int corePoolSize;
private final int maxSize;
private final int timeout;
private fianl TimeUnit timeUnit;
private final BlockingQueue<Runnable> blockingQueue;
private final RejectHandle rejectHandle;
private final ThreadFactory threadFactory;
public MyThreadPool (int corePoolSize, int maxSize, int timeout, TimeUnit timeUnit, BlockingQueue<Runnable> blockingQueue, RejectHandle rejectHandle, ThreadFactory threadFactory) {
this.corePoolSize = corePoolSize;
this.maxSize = maxSize;
this.timeout = timeout;
this.timeUnit = timeUnit;
this.blockingQueue = blockingQueue;
this.rejectHandle = rejectHandle;
this.threadFactory = threadFactory;
}
List<Thread> coreList = new ArrayList<>();
List<Thread> supportList = new ArrayList<>();
void.execute(Runnable command) {
if (coreList.size() < corePoolSize) {
Thread thread = threadFactory.newThread(new CoreTask());
coreList.add(thread);
thread.start();
}
if (blockingQueue.offer(command)) {
return;
}
if (coreList.size() + supportList.size() < maxSize) {
Thread thread = threadFactory.newThread(new SupportThread());
supportList.add(thread);
thread.start();
}
if (!blockingQueue.offer(command)) {
rejectHandle.reject(command, this);
}
}
class CoreThread extends Thread {
@Override
public void run() {
while (true) {
try {
Runnable command = blockingQueue.take();
command.run();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
class SupportThread extends Thread {
@Override
public void run() {
while (true) {
try {
Runnable command = blockingQueue.poll(timeout, timeUnit);
if (null == command) {
break;
}
command.run();
} catch (InterruptedException e) {
throw new RuntimeException(e);
};
System.out.println(Thread.currentThread().getName() + "线程结束");
}
}
}
}