ThreadPoolExecutor使用说明

解释说明



测试验证代码


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异常的原因。

可是即使使用了上面的代码,还是不能保证在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;
}
});

等待池中的线程终止
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)


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值