在阿里巴巴开发手册中不建议使用 Executor:
因为默认的 Executors 线程池底层是基于 ThreadPoolExecutor 构造函数封装的,采用无界队列存放缓存任务,会无限缓存任务容易发生 内存溢出,会导致我们最大线程数会失效
线程池底层 ThreadPoolExecutor 实现原理
- 当线程数小于核心线程数时,创建线程。
- 当线程数大于等于核心线程数,且任务队列未满时,将任务放入任务队列。
- 当线程数大于等于核心线程数,且任务队列已满
3.1 若线程数小于最大线程数,创建线程
3.2 若线程数等于最大线程数,抛出异常,拒绝任务
ThreadPoolExecutor 核心参数:
- corePoolSize:核心线程数量 一直正在保持运行的线程
- maximumPoolSize:最大线程数,线程池允许创建的最大线程数。
- keepAliveTime:超出 corePoolSize 后创建的线程的存活时间。
- unit:keepAliveTime 的时间单位。
- workQueue:任务队列,用于保存待执行的任务。
- threadFactory:线程池内部创建线程所用的工厂。
- handler:任务无法执行时的处理器
线程池拒绝策略类型:
- AbortPolicy 丢弃任务,抛运行时异常
- CallerRunsPolicy 执行任务
- DiscardPolicy 忽视,什么都不会发生
- DiscardOldestPolicy 从队列中踢出最先进入队列(最后一个执行)的任务
- 实现 RejectedExecutionHandler 接口,可自定义处理器(推荐)
代码实现:
先创建自定义线程池拒绝策略类实现RejectedExecutionHandler
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
/**
* 实现 RejectedExecutionHandler 接口,可自定义处理器
* 自定义线程池拒绝策略
*/
public class MyExecutionHandler implements ThreadFactory, RejectedExecutionHandler {
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
System.out.println(r.getClass()+":任务已满,自定义拒绝线程任务,阻塞3秒后重新放入队列");
// r.run();//如果直接r.run()则由主线程main来执行该次溢出任务
try {
//此时队列满我们将阻塞3秒再将线程任务重新放回线程池中去
Thread.sleep(3000L);
executor.execute(r);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Override
public Thread newThread(Runnable r) {
return null;
}
}
在创建自定义线程池:
import java.util.concurrent.*;
public class MyThreadPoolExecutor {
public static ExecutorService newFixedThreadPool(int corePoolSize, int maximumPoolSize, int blockingQueue) {
/* return new ThreadPoolExecutor(corePoolSize, maximumPoolSize,
60L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(blockingQueue), (RejectedExecutionHandler) new ThreadPoolExecutor.DiscardPolicy ());//拒绝策略为放弃任务*/
return new ThreadPoolExecutor(corePoolSize, maximumPoolSize,
60L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(blockingQueue),(RejectedExecutionHandler)new MyExecutionHandler());
}
}
这样我们自定义线程池就完成啦!然后测试下:
public class Test {
public static void main(String[] args) throws InterruptedException {
//1.提交的线程任务数<核心线程数 (核心线程数任务复用)
//2.提交的线程任务数>核心线程数 且我们队列容量没有满 将该任务缓存到我们队列中
// 循环3 4 5 6 7 缓存到我们队列中
//3.提交的线程任务数>核心线程数 且我们队列容量满了
//8,9,10
// 最多在额外创建两个线程 4-2 2个线程
// 2个线程 8 ,9
// 10个任务----拒绝
ExecutorService executorService = MyThreadPoolExecutor.newFixedThreadPool(2,4,5);
for (int i = 1; i <= 10; i++) {
final int finalI = i;
executorService.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "," + finalI);
}
});
}
Thread.sleep(1000L);
//返回池中有史以来最大的线程数
System.out.println(((ThreadPoolExecutor)executorService).getLargestPoolSize());
// 实际上最多执行多少个任务 核心线程数+缓存队列的容量+最大线程数-核心线程数
}
}
运行结果截图: