解释说明
测试验证代码
package zdb;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class ThreadPoolExecutorTest {
public static Task task1 = new Task("corePoolSize-1");
public static Task task2 = new Task("corePoolSize-2");
public static Task task3 = new Task("corePoolSize-3");
public static Task task4 = new Task("corePoolSize-4");
public static Task task5 = new Task("corePoolSize-5");
public static Task task6 = new Task("corePoolSize-6");
public static Task task7 = new Task("corePoolSize-7");
public static Task task8 = new Task("corePoolSize-8");
public static Task task9 = new Task("corePoolSize-9");
public static Task task10 = new Task("corePoolSize-10");
public static Task task11 = new Task("queue-1");
public static Task task12 = new Task("queue-2");
public static Task task13 = new Task("queue-3");
public static Task task14 = new Task("queue-4");
public static Task task15 = new Task("queue-5");
public static Task task16 = new Task("maxinumPoolSize-1");
public static Task task17 = new Task("maxinumPoolSize-2");
public static void main(String[] args) throws InterruptedException {
BlockingQueue<Runnable> queue = new LinkedBlockingQueue<Runnable>(5);
ThreadPoolExecutor executor = new ThreadPoolExecutor(10, 12, 3,
TimeUnit.SECONDS, queue,
new ThreadPoolExecutor.CallerRunsPolicy() {
public void rejectedExecution(Runnable r,
ThreadPoolExecutor e) {
if (!e.isShutdown()) {
((Task)r).setRunning(false);
r.run();
}
}
});
log("开始", executor);
// 如下10个任务把corePoolSize填满
executor.execute(task1); // task1 -> corePool[0]
executor.execute(task2); // task2 -> corePool[1]
executor.execute(task3); // task3 -> corePool[2]
executor.execute(task4); // task4 -> corePool[3]
executor.execute(task5); // task5 -> corePool[4]
executor.execute(task6); // task6 -> corePool[5]
executor.execute(task7); // task7 -> corePool[6]
executor.execute(task8); // task8 -> corePool[7]
executor.execute(task9); // task9 -> corePool[8]
executor.execute(task10);// task10 -> corePool[9]
// 如下5个任务把队列挤满
Thread.sleep(2000);
log("队列开始存放", executor);
executor.execute(task11); // task11 -> queue[0]
executor.execute(task12); // task12 -> queue[1]
executor.execute(task13); // task13 -> queue[2]
executor.execute(task14); // task14 -> queue[3]
executor.execute(task15); // task15 -> queue[4]
log("队列存放结束", executor);
// 如下两个任务达到maxinumPoolSize
Thread.sleep(2000);
executor.execute(task16);// task16 -> maxinumPool[0]
executor.execute(task17);// task17 -> maxinumPool[1]
Thread.sleep(100);
log("corePoolSize已满,存放到 maxinumPool", executor);
// 超出maxinumPoolSize由RejectedExecutionHandler处理
executor.execute(new Task("rejectedExecution - 1"));
Thread.sleep(100);
log("超出maxinumPoolSize要求", executor);
// 结束一些线程,腾出线程池空间,使队列清空,队列 的任何都进入到线程中开始 执行
task1.setRunning(false); // queue[0](task11) -> corePool[0]
Thread.sleep(100);
task2.setRunning(false); // queue[1](task12) -> corePool[1]
Thread.sleep(100);
task3.setRunning(false); // queue[2](task13) -> corePool[2]
Thread.sleep(100);
task4.setRunning(false); // queue[3](task14) -> corePool[3]
// maxinumPoolSize线程池中的线程复用
Thread.sleep(100);
task16.setRunning(false); // queue[4](task15) -> maxinumPool[0]
Thread.sleep(1000);
log("队列清空", executor);
log("队列run选择",executor);
task15.setRunning(false); // maxinumPool[0] = 空任务
Thread.sleep(4000);
log("超出corePoolSize,超出keepAlive时间(3),自动销户超出corePoolSize的空闲线程", executor);
executor.shutdown();
}
public static void log(String msg, ThreadPoolExecutor executor) {
System.out.println(msg + " poolSize:" + executor.getPoolSize() + ","
+ "activeCount:" + executor.getActiveCount() + ","
+ "taskCount" + executor.getTaskCount() + "," + "queueSize:"
+ executor.getQueue().size());
}
public static class Task implements Runnable {
private String taskName;
private boolean running=true;
public Task(String taskName) {
super();
this.taskName = taskName;
}
@Override
public void run() {
try {
System.out.println("task:" + taskName+" start...");
while (this.running) {
Thread.sleep(10);
}
System.out.println("task:" + taskName+" complete.");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public boolean isRunning() {
return running;
}
public void setRunning(boolean running) {
this.running = running;
}
}
}
通常终止线程池使用如下方法:
executor.shutdown();
executor.shutdownNow();
这个两个方法都是终止线程池,但是有区别的。shutdown()方法只是阻止新的任务加入,shutdownNow()不但阻止新的任务加入,而且会给线程池中的每个任务(线程)发送一个中断信号。并返回线程次中还有没执行的任务。
我们大家都知道,java是不可以能一个线程让另一个线程终止执行的,只能是发送一个中断信号,线程根据自身运行的情况来决定是否中断(退出)。
那么这就引出了问题?
如果一个任务在线程池中不间断的运行(例如:队列的接收并处理任务),那么单独的代码executor.shutdown();是不能中断池中的线程运行。我们通常的手法是声明一个volatile boolean running = false; 利用volatile的特性(可见性),来保证池的线程退出,实例代码如下:
池中线程任务:
public void run() {
while(running){
xxxxxxx;
}
}
主线程关闭池方法:
public void stop(){
running = false;
executor.shutdown();
}
如果你以为上面的代码已经可以保证线程的安全的退出了,那么这个以为是错误的?
如果while(running){循环执行的代码},while中的代码如果有阻塞代码出现,那么这个池中的线程永远也无法关闭(任务无法结束),也就是说永远执行不到while的判断。
我们对上面的代码进行改善,加入中断的验证和处理,示例代码如下:
volatile boolean running = false;
public void run(){
while(running &&
!Thread.interrupted()){
try{
xxxxxxxxxxx;
}catch(
InterruptedException ex){
running = false;
}
}
}
public void stop(){
running = false;
executor.
shutdownNow();
}
shutdownNow()执行后会对池内的每个线程发出中断信号,线程上的任务判断是否有中断信号,根据具体情况来决定是否退出。同样还可以保证在线程任务上有阻塞的代码,
也可以安全退出,因为java代码库提供的所有的阻塞类和方法都提供了中断识别,这也就是调用java阻塞代码需要处理InterruptedException异常的原因。
也可以安全退出,因为java代码库提供的所有的阻塞类和方法都提供了中断识别,这也就是调用java阻塞代码需要处理InterruptedException异常的原因。
可是即使使用了上面的代码,还是不能保证在jvm退出前,池安全的退出。设想一下如果while中的代码不断的执行,执行不到running &&
!Thread.interrupted()验证。那么即使调用了shutdownNow(),池也不能关闭(池内的线程还在运行)这是很危险的。
有两种办法解决上面的问题:
1.编写任务程序的时候,根据任务的具体情况在适当的位置加入running的判断。
2.使用精灵线程(daemon),精灵线程保证jvm执行退出时,精灵线程自动关闭。但这要非常的小心,任务是在不受控的情况下退出的。
那么如果创建线程池的线程是精灵线程?
private ExecutorService executor = Executors.newSingleThreadExecutor(new ThreadFactory() {
@Override
public Thread newThread(Runnable runnable) {
Thread t = new Thread(runnable);
t.setDaemon(true);
return t;
}
});
@Override
public Thread newThread(Runnable runnable) {
Thread t = new Thread(runnable);
t.setDaemon(true);
return t;
}
});
等待池中的线程终止
executor.awaitTermination(2000, TimeUnit.MICROSECONDS);,这里是等待2000毫秒(最长超时时间)。这个方法只是一个简单的等待,没有其他任何意义。返回值boolean,说明线程池在等待的时间内是否完成。
下面给出一个无法退出的jvm的栈信息:
awaitTermination设置过大,而且池内的任务还不能结束。
"main" prio=10 tid=0x0000000041aba000 nid=0x5f53 waiting on condition [0x00002b3d2a73f000]
java.lang.Thread.State: TIMED_WAITING (parking)
at sun.misc.Unsafe.park(Native Method)
- parking to wait for <0x0000000733e27db8> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
at java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:196)
at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.awaitNanos(AbstractQueuedSynchronizer.java:2025)
at java.util.concurrent.ThreadPoolExecutor.awaitTermination(ThreadPoolExecutor.java:1261)
at java.util.concurrent.Executors$DelegatedExecutorService.awaitTermination(Executors.java:596)
at teps.common.jtbhlht.api.impl.JtbDataCollectServiceDataProvider.stop(JtbDataCollectServiceDataProvider.java:181)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor$LifecycleElement.invoke(InitDestroyAnnotationBeanPostProcessor.java:344)
at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor$LifecycleMetadata.invokeDestroyMethods(InitDestroyAnnotationBeanPostProcessor.java:309)
at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor.postProcessBeforeDestruction(InitDestroyAnnotationBeanPostProcessor.java:148)
at org.springframework.beans.factory.support.DisposableBeanAdapter.destroy(DisposableBeanAdapter.java:220)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.destroyBean(DefaultSingletonBeanRegistry.java:510)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.destroySingleton(DefaultSingletonBeanRegistry.java:486)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.destroySingleton(DefaultListableBeanFactory.java:742)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.destroySingletons(DefaultSingletonBeanRegistry.java:455)
at org.springframework.context.support.AbstractApplicationContext.destroyBeans(AbstractApplicationContext.java:1090)
at org.springframework.context.support.AbstractApplicationContext.doClose(AbstractApplicationContext.java:1064)
at org.springframework.context.support.AbstractApplicationContext.close(AbstractApplicationContext.java:1010)
- locked <0x0000000730564340> (a java.lang.Object)
at org.springframework.web.context.ContextLoader.closeWebApplicationContext(ContextLoader.java:559)
at org.springframework.web.context.ContextLoaderListener.contextDestroyed(ContextLoaderListener.java:143)
at org.apache.catalina.core.StandardContext.listenerStop(StandardContext.java:4249)
at org.apache.catalina.core.StandardContext.stop(StandardContext.java:4890)
- locked <0x00000007301f9660> (a org.apache.catalina.core.StandardContext)
at org.apache.catalina.core.ContainerBase.stop(ContainerBase.java:1110)
- locked <0x00000007302d3970> (a org.apache.catalina.core.StandardHost)
at org.apache.catalina.core.ContainerBase.stop(ContainerBase.java:1110)
- locked <0x00000007307fff28> (a org.apache.catalina.core.StandardEngine)
at org.apache.catalina.core.StandardEngine.stop(StandardEngine.java:468)
at org.apache.catalina.core.StandardService.stop(StandardService.java:604)
- locked <0x00000007307fff28> (a org.apache.catalina.core.StandardEngine)
at org.apache.catalina.core.StandardServer.stop(StandardServer.java:788)
at org.apache.catalina.startup.Catalina.stop(Catalina.java:662)
at org.apache.catalina.startup.Catalina.start(Catalina.java:629)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.apache.catalina.startup.Bootstrap.start(Bootstrap.java:289)
at org.apache.catalina.startup.Bootstrap.main(Bootstrap.java:414)
java.lang.Thread.State: TIMED_WAITING (parking)
at sun.misc.Unsafe.park(Native Method)
- parking to wait for <0x0000000733e27db8> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
at java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:196)
at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.awaitNanos(AbstractQueuedSynchronizer.java:2025)
at java.util.concurrent.ThreadPoolExecutor.awaitTermination(ThreadPoolExecutor.java:1261)
at java.util.concurrent.Executors$DelegatedExecutorService.awaitTermination(Executors.java:596)
at teps.common.jtbhlht.api.impl.JtbDataCollectServiceDataProvider.stop(JtbDataCollectServiceDataProvider.java:181)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor$LifecycleElement.invoke(InitDestroyAnnotationBeanPostProcessor.java:344)
at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor$LifecycleMetadata.invokeDestroyMethods(InitDestroyAnnotationBeanPostProcessor.java:309)
at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor.postProcessBeforeDestruction(InitDestroyAnnotationBeanPostProcessor.java:148)
at org.springframework.beans.factory.support.DisposableBeanAdapter.destroy(DisposableBeanAdapter.java:220)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.destroyBean(DefaultSingletonBeanRegistry.java:510)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.destroySingleton(DefaultSingletonBeanRegistry.java:486)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.destroySingleton(DefaultListableBeanFactory.java:742)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.destroySingletons(DefaultSingletonBeanRegistry.java:455)
at org.springframework.context.support.AbstractApplicationContext.destroyBeans(AbstractApplicationContext.java:1090)
at org.springframework.context.support.AbstractApplicationContext.doClose(AbstractApplicationContext.java:1064)
at org.springframework.context.support.AbstractApplicationContext.close(AbstractApplicationContext.java:1010)
- locked <0x0000000730564340> (a java.lang.Object)
at org.springframework.web.context.ContextLoader.closeWebApplicationContext(ContextLoader.java:559)
at org.springframework.web.context.ContextLoaderListener.contextDestroyed(ContextLoaderListener.java:143)
at org.apache.catalina.core.StandardContext.listenerStop(StandardContext.java:4249)
at org.apache.catalina.core.StandardContext.stop(StandardContext.java:4890)
- locked <0x00000007301f9660> (a org.apache.catalina.core.StandardContext)
at org.apache.catalina.core.ContainerBase.stop(ContainerBase.java:1110)
- locked <0x00000007302d3970> (a org.apache.catalina.core.StandardHost)
at org.apache.catalina.core.ContainerBase.stop(ContainerBase.java:1110)
- locked <0x00000007307fff28> (a org.apache.catalina.core.StandardEngine)
at org.apache.catalina.core.StandardEngine.stop(StandardEngine.java:468)
at org.apache.catalina.core.StandardService.stop(StandardService.java:604)
- locked <0x00000007307fff28> (a org.apache.catalina.core.StandardEngine)
at org.apache.catalina.core.StandardServer.stop(StandardServer.java:788)
at org.apache.catalina.startup.Catalina.stop(Catalina.java:662)
at org.apache.catalina.startup.Catalina.start(Catalina.java:629)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.apache.catalina.startup.Bootstrap.start(Bootstrap.java:289)
at org.apache.catalina.startup.Bootstrap.main(Bootstrap.java:414)