Spring的ThreadPoolTaskExecutor:
ThreadPoolTaskExecutor是借助JDK并发包中的ThreadPoolExecutor,类ThreadPoolTaskExecutor中包含ThreadPoolExecutor。
</pre><pre code_snippet_id="1576841" snippet_file_name="blog_20160211_1_1277057" name="code" class="java"><?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd"
default-lazy-init="false">
<!-- callerRunsPolicy:主线程直接执行该任务,执行完之后尝试添加下一个任务到线程池中,可以有效降低向线程池内添加任务的速度 -->
<bean id="callerRunsPolicy" class="java.util.concurrent.ThreadPoolExecutor.CallerRunsPolicy"/>
<!-- 线程池配置 -->
<bean id="asynThreadPool" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
<!-- 线程组&名称 -->
<property name="threadGroupName" value="epp-tg1"/>
<property name="threadNamePrefix" value="remote-query-task"/>
<!-- 线程池维护线程的最少数量 -->
<property name="corePoolSize" value="1"/>
<!-- 线程池维护线程所允许的空闲时间,5分钟 -->
<property name="keepAliveSeconds" value="300"/>
<!-- 线程池维护线程的最大数量 -->
<property name="maxPoolSize" value="200"/>
<!-- 线程池所使用的缓冲队列 -->
<property name="queueCapacity" value="100"/>
<!-- CallerRunsPolicy -->
<property name="rejectedExecutionHandler" ref="callerRunsPolicy"/>
</bean>
</beans>
(1) 线程池数量小于corePoolSize,则增加线程。即使线程池中的线程都处于空闲状态,也要创建新的线程来处理。
(2) 如果线程数量等于corePoolSize,则把请求放在workQueue中,池子里的线程空闲线程就去从workQueue中取任务并处理。
(3) 如果workQueue已满,则新建线程加入线程池,并处理请求,如果池子的大小撑到了maxPoolsize,就用rejectedExecutionHangdler 指定的策略来处理此任务。
(4) 如果当线程数大于corePoolsize的时候,多余的线程会等待keepAliveTime长时间,如果无请求可处理就自行销毁。(线程数小于corePoolSize时,线程不会自动销毁,除非手动调用对应的方法)
总结:
处理任务的优先级为:核心线程corePoolSize、任务队列workQueue,最大线程maxPoolSize,如果三者都满了,使用handle处理被拒绝的任务。
测试:
Java配置:
<bean id="asynThreadPool" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
<!-- 线程组&名称 -->
<property name="threadGroupName" value="epp-tg1"/>
<property name="threadNamePrefix" value="remote-query-task"/>
<!-- 线程池维护线程的最少数量 -->
<property name="corePoolSize" value="1"/>
<!-- 线程池维护线程所允许的空闲时间,5分钟 -->
<property name="keepAliveSeconds" value="1"/>
<!-- 线程池维护线程的最大数量 -->
<property name="maxPoolSize" value="100"/>
<!-- 线程池所使用的缓冲队列 -->
<property name="queueCapacity" value="3"/>
<!-- CallerRunsPolicy -->
<property name="rejectedExecutionHandler" ref="callerRunsPolicy"/>
</bean>
代码:
ApplicationContext applicationContext =new ClassPathXmlApplicationContext("multiThread/spring-biz-threadpools.xml");
ThreadPoolTaskExecutor asynThreadPool=(ThreadPoolTaskExecutor) applicationContext.getBean("asynThreadPool");
List<String> result =new ArrayList<>();
List<Future<String>> list = new ArrayList<>();
for (int i=0;i<10;++i){
Future<String> 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代码:
<pre name="code" class="java"> @Test
public void test() throws ExecutionException, InterruptedException {
ApplicationContext applicationContext =new ClassPathXmlApplicationContext("multiThread/spring-biz-threadpools.xml");
ThreadPoolTaskExecutor asynThreadPool=(ThreadPoolTaskExecutor) applicationContext.getBean("asynThreadPool");
List<String> result =new ArrayList<>();
List<Future<String>> list = new ArrayList<>();
for (int i=0;i<10;++i){
Future<String> future = asynThreadPool.submit(()->methodTest());
System.out.println("活跃线程数:"+Thread.activeCount());
list.add(future);
}
for(Future<String> 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<T> ,通过future类的get方法获取到返回值
(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原理)