Spring的ThreadPoolTaskExecutor:
ThreadPoolTaskExecutor是借助JDK并发包中的ThreadPoolExecutor,类ThreadPoolTaskExecutor中包含ThreadPoolExecutor。
(1) 线程池数量小于corePoolSize,则增加线程。即使线程池中的线程都处于空闲状态,也要创建新的线程来处理。
(2) 如果线程数量等于corePoolSize,则把请求放在workQueue中,池子里的线程空闲线程就去从workQueue中取任务并处理。
(3) 如果workQueue已满,则新建线程加入线程池,并处理请求,如果池子的大小撑到了maxPoolsize,就用rejectedExecutionHangdler 指定的策略来处理此任务。
(4) 如果当线程数大于corePoolsize的时候,多余的线程会等待keepAliveTime长时间,如果无请求可处理就自行销毁。(线程数小于corePoolSize时,线程不会自动销毁,除非手动调用对应的方法)
总结:
处理任务的优先级为:核心线程corePoolSize、任务队列workQueue,最大线程maxPoolSize,如果三者都满了,使用handle处理被拒绝的任务。
测试:
Java配置:
代码:
ApplicationContext applicationContext =new ClassPathXmlApplicationContext("multiThread/spring-biz-threadpools.xml");
ThreadPoolTaskExecutor asynThreadPool=(ThreadPoolTaskExecutor) applicationContext.getBean("asynThreadPool");
List result =new ArrayList<>();
List> list = new ArrayList<>();
for (int i=0;i<10;++i){
Future future = asynThreadPool.submit(()->methodTest());
System.out.println("活跃线程数:"+Thread.activeCount());
list.add(future);
}
运行结果:
活跃线程数:3
活跃线程数:3
活跃线程数:3
活跃线程数:3
活跃线程数:4
活跃线程数:5
活跃线程数:6
备注:活跃线程数包含主线程。
任务1,新建线程,当前活跃线程数为2;
任务2,corePoolSize已满,所以会将任务放在workQueue中,当前活跃线程数为2,
任务3,corePoolSize已满,所以会将任务放在workQueue中,当前活跃线程数为2,
任务4:corePoolSize已满,所以会将任务放在workQueue中,当前活跃线程数为2,
任务5:corePoolSize已满,workQueue已满,还未达到maxPoolSize,所以会新建线程,
.......
java代码:
@Test
public void test() throws ExecutionException, InterruptedException {
ApplicationContext applicationContext =new ClassPathXmlApplicationContext("multiThread/spring-biz-threadpools.xml");
ThreadPoolTaskExecutor asynThreadPool=(ThreadPoolTaskExecutor) applicationContext.getBean("asynThreadPool");
List result =new ArrayList<>();
List> list = new ArrayList<>();
for (int i=0;i<10;++i){
Future future = asynThreadPool.submit(()->methodTest());
System.out.println("活跃线程数:"+Thread.activeCount());
list.add(future);
}
for(Future future:list){
try{
System.out.println(future.get());
}catch (Exception e){
e.printStackTrace();
}
}
Thread.sleep(100000);
System.out.println("test");
}
public String methodTest() throws Exception {
System.out.println("thread test "+i );
Thread.sleep(10000);
return "call()方法被自动调用,线程名称:" + Thread.currentThread().getName();
}
注意:
如果线程抛出异常,在调用future.get()方法时,会将异常抛出到外部,抛出的异常为ExecutionException。在调用future.get()方法时,会等待线程运行完成,并取得返回结果。
通过e.getCause取出导致抛出异常的原因。
submit和execute的区别:
(1) 接受的参数不一样
submit接受的参数:接受runable或者Callable对象
execute接收的参数:接收runnable对象
(2) submit有返回值,execute无返回值
submit的返回值未Future
(3) submit处理exception的方式和execute不同
submit处理异常:如果某个线程抛出异常,则通过future.get()获取到该异常
execute() 无法返回异常,run方法不能抛出异常,所以方法内部必须捕获异常。
runnable和callable的差别:
(1) Callable规定的方法是call(), Runnable规定的方法是run
(2) Callable的任务执行后可返回值,而runnable的认识是不能返回值的
(3) call方法可以抛出异常,run方法不可以
(4)运行Callable任务可以拿到一个Future对象,Future表示异步计算的结果,可通过get()方法取出返回值,如果线程出现异常,Future.get()会抛出InterruptedException或者ExecutionException,如果线程已取消,会抛出CancellationException。
线程容量已经达到饱和时,对于再到达的线程的处理策略:
当 Executor 已经关闭,并且 Executor 将有限边界用于最大线程和工作队列容量,且已经饱和时,在方法 execute(java.lang.Runnable) 中提交的新任务将被拒绝。在以上两种情况下,execute 方法都将调用其RejectedExecutionHandler 的RejectedExecutionHandler.rejectedExecution(java.lang.Runnable, java.util.concurrent.ThreadPoolExecutor) 方法。下面提供了四种预定义的处理程序策略:
(1)在默认的 ThreadPoolExecutor.AbortPolicy 中,处理程序遭到拒绝将抛出运行时RejectedExecutionException。
(2) 在 ThreadPoolExecutor.CallerRunsPolicy 中,线程调用运行该任务的execute 本身。此策略提供简单的反馈控制机制,能够减缓新任务的提交速度。
(3) 在 ThreadPoolExecutor.DiscardPolicy 中,不能执行的任务将被删除。
(4) 在 ThreadPoolExecutor.DiscardOldestPolicy 中,如果执行程序尚未关闭,则位于工作队列头部的任务将被删除,然后重试执行程序(如果再次失败,则重复此过程)。
定义和使用其他种类的 RejectedExecutionHandler 类也是可能的,但这样做需要非常小心,尤其是当策略仅用于特定容量或排队策略时。
ThreadPoolExecutor类:
http://www.importnew.com/17633.html (对应的英文文章:https://blog.bramp.net/post/2015/12/17/the-importance-of-tuning-your-thread-pools/)
http://www.importnew.com/17820.html(主要讲讲解了java的ThreadPoolExecutor原理)