之前在公众号看到有关的内容,然后晚上抽了个时间试了下.故记录下
相关知识
- Executors线程池
- IO密集型和CPU密集型任务的线程配置
- 自定义线程池执行流程
三种队列,四种策略,五种线程池
//三种阻塞队列:
BlockingQueue workQueue = null;
workQueue = new ArrayBlockingQueue<>(5);//基于数组的先进先出队列,有界
workQueue = new LinkedBlockingQueue<>();//基于链表的先进先出队列,无界
workQueue = new SynchronousQueue<>();//无缓冲的等待队列,无界
//四种拒绝策略:
RejectedExecutionHandler rejected = null;
rejected = new ThreadPoolExecutor.AbortPolicy();//默认,队列满了丢任务抛出异常
rejected = new ThreadPoolExecutor.DiscardPolicy();//队列满了丢任务不异常
rejected = new ThreadPoolExecutor.DiscardOldestPolicy();//将最早进入队列的任务删,之后再尝试加入队列
rejected = new ThreadPoolExecutor.CallerRunsPolicy();//如果添加到线程池失败,那么主线程会自己去执行该任务
//五种线程池:
ExecutorService threadPool = null;
threadPool = Executors.newCachedThreadPool();//有缓冲的线程池,线程数 JVM 控制
threadPool = Executors.newFixedThreadPool(3);//固定大小的线程池
threadPool = Executors.newScheduledThreadPool(2);
threadPool = Executors.newSingleThreadExecutor();//单线程的线程池,只有一个线程在工作
threadPool = new ThreadPoolExecutor();//默认线程池,可控制参数比较多
验证:
- execute:会抛出异常,submit: 不会抛出异常
public static void main(String[] args) throws Exception {
ThreadPoolTaskExecutor threadPoolTaskExecutor = buildThreadPoolTaskExecutor();
System.out.println("MaxPoolSize:" + threadPoolTaskExecutor.getMaxPoolSize() + "!");
//正常情况,不会抛出异常信息
threadPoolTaskExecutor.submit(() -> sayHi("submit"));
//会跑出堆栈异常信息
threadPoolTaskExecutor.execute(() -> sayHi("execute"));
System.out.println("------");
//使用 future.get() 能获取到submit排除的异常堆栈信息
//并且可以获取接口返回信息
Future> future = threadPoolTaskExecutor.submit(new ThreadPoolRunable("submit"));
try {
//因为runnable是没有返回值的,所以返回是null
System.out.println("future get str:" + future.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
System.out.println("------");
//callable 可以获取接口的返回值
Future> futureByCallAbleSubmit = threadPoolTaskExecutor.submit(new ThreadPoolCallable("callable"));
//可以获取callable的返回结果
System.out.println("callable return str:" + futureByCallAbleSubmit.get());
}
public static void sayHi(String name) {
String returnStr = "this methon:" + name;
System.out.println(returnStr);
throw new RuntimeException(returnStr + "--->Exception");
}
private static ThreadPoolTaskExecutor buildThreadPoolTaskExecutor() {
//一个线程池中的线程异常了,那么线程池会怎么处理这个线程? 问题验证
ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
//线程池数量
threadPoolTaskExecutor.setCorePoolSize(10);
//任务执行最大线程数
threadPoolTaskExecutor.setMaxPoolSize(10);
//线程池缓存任务队列大小
threadPoolTaskExecutor.setQueueCapacity(200);
//线程允许空闲时长 (单位秒 SECONDS)
threadPoolTaskExecutor.setKeepAliveSeconds(20);
//线程超过空闲时间限制,均会退出直到线程数量为0
threadPoolTaskExecutor.setAllowCoreThreadTimeOut(true);
//对Runnable任务装饰一下, 在任务执行时完成异常日志打印、ThreadLocal清理等功能
threadPoolTaskExecutor.setTaskDecorator(null);
//线程异常处理策略
/**
* 四种线程的拒绝策略
*
* new ThreadPoolExecutor.AbortPolicy();//默认,队列满了丢任务抛出异常
* new ThreadPoolExecutor.DiscardPolicy();//队列满了丢任务不异常
* new ThreadPoolExecutor.DiscardOldestPolicy();//将最早进入队列的任务删,之后再尝试加入队列
* new ThreadPoolExecutor.CallerRunsPolicy();//如果添加到线程池失败,那么主线程会自己去执行该任务
*/
threadPoolTaskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());
//初始化线程池
threadPoolTaskExecutor.initialize();
return threadPoolTaskExecutor;
}
执行结果:
- 线程池中一个线程异常,不会影响其他线程任务
public static void main(String[] args) throws Exception {
ThreadPoolTaskExecutor threadPoolTaskExecutor = buildThreadPoolTaskExecutor();
threadPoolTaskExecutor.execute(() -> sayHi("execute"));
threadPoolTaskExecutor.execute(() -> sayHi("execute"));
threadPoolTaskExecutor.execute(() -> sayHi("execute-e"));
threadPoolTaskExecutor.execute(() -> sayHi("execute"));
threadPoolTaskExecutor.execute(() -> sayHi("execute"));
}
public static void sayHi(String name) {
if("execute-e".equals(name)){
throw new RuntimeException(Thread.currentThread().getName()+"---"+name);
}else{
System.out.println(Thread.currentThread().getName()+"---"+name);
}
}
- 线程池异常的线程,不会重新放入线程池,会删除掉重新创建
/**
/**
* This method removes thread from worker set 如果出现异常,会将此线程移除.并重新创建线程放入线程池
*
* @param w the worker
* @param completedAbruptly if the worker died due to user exception
*/
private void processWorkerExit(Worker w, boolean completedAbruptly) {
if (completedAbruptly) // If abrupt, then workerCount wasn't adjusted
decrementWorkerCount();
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
completedTaskCount += w.completedTasks;
workers.remove(w);
} finally {
mainLock.unlock();
}
tryTerminate();
int c = ctl.get();
if (runStateLessThan(c, STOP)) {
if (!completedAbruptly) {
int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
if (min == 0 && ! workQueue.isEmpty())
min = 1;
if (workerCountOf(c) >= min)
return; // replacement not needed
}
addWorker(null, false);
}
}
*/
结论:
当一个线程池里面的线程异常后:执行方式是execute时,可以看到堆栈异常的输出。当执行方式是submit时,堆栈异常没有输出。但是调用Future.get()方法时,可以捕获到异常。不会影响线程池里面其他线程的正常执行。线程池会把这个线程移除掉,并创建一个新的线程放到线程池中。