Java线程基础——线程池使用示例

阅读更多

引言

合理利用线程池能够带来三个好处:

第一:降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。第二:提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。第三:提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。

 

本篇参考:http://www.infoq.com/cn/articles/java-threadPool

                  http://www.cjsdn.net/Doc/JDK60/

 

关于线程池的配置参数

Java中创建线程池创建如下:

ThreadPoolExecutor executor = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler);

 ThreadPoolExecutor的构造函数有多个,通过构造函数的参数配置线程池,以下是各个参数的说明:

corePoolSize:线程池的基本大小,maximumPoolSize:线程池最大大小,即线程池允许创建的最大线程数,线程池会根据corePoolSize(调用getCorePoolSize())和maximumPoolSize(调用getMaximumPoolSize())去自动调整池中的线程数量(调用getPoolSize())。当池中的线程数少于corePoolSize的时候,会创建新的线程放到任务队列处理请求,不管队列中其他的线程是否空闲;当线程数大于coolPoolSize而小于maximumPoolSize的时候,如果池中任务队列为空,则创建线程放入任务队列。当设置maximumPoolSize为Integer.MAX_VALUE,则这个线程池为无界线程池,然后池中的线程数量是任意的。如果任务队列满了,并且已创建的线程数小于最大线程数,则线程池会再创建新的线程执行任务,该线程等待进入任务队列。值得注意的是如果使用了无界的任务队列这个参数就没什么效果。(鄙人估计corePoolSize确定了任务队列的大小)

 

workQueue:任务队列,用于转移和阻塞提交了的任务,即任务队列是运行线程的,任务队列根据corePoolSize和maximumPoolSize工作:

   1.当正在运行的线程小于coolPoolSize,线程池会创建新的线程。

   2.当大于coolPoolSize而任务队列未满,则从队列里拿一个空闲的线程去接任务。

   3.当大于coolPoolSize而任务队列满了(即队列中没有空闲的线程),并且小于maximumPoolSize,会创建新的线程接任务,该线程等待进入任务队列。

   4.当大于maximumPoolSize,该任务会根据handler(RejectedExecutionHandler,饱和策略)处理。

参考图:
Java线程基础——线程池使用示例_第1张图片
 任务队列又有以下集中策略模式:

    1.直接提交。工作队列的默认选项是 SynchronousQueue,它将任务直接提交给线程而不保持它们。在此,如果不存在可用于立即运行任务的线程,则试图把任务加入队列将失败,因此会构造一个新的线程。此策略可以避免在处理可能具有内部依赖性的请求集时出现锁。直接提交通常要求无界 maximumPoolSizes 以避免拒绝新提交的任务。当命令以超过队列所能处理的平均数连续到达时,此策略允许无界线程具有增长的可能性。    

    2.无界队列。使用无界队列(例如,不具有预定义容量的 LinkedBlockingQueue)将导致在所有 corePoolSize 线程都忙时新任务在队列中等待。这样,创建的线程就不会超过 corePoolSize。(因此,maximumPoolSize 的值也就无效了。)当每个任务完全独立于其他任务,即任务执行互不影响时,适合于使用无界队列;例如,在 Web 页服务器中。这种排队可用于处理瞬态突发请求,当命令以超过队列所能处理的平均数连续到达时,此策略允许无界线程具有增长的可能性。

  3. 有界队列。当使用有限的 maximumPoolSizes 时,有界队列(如 ArrayBlockingQueue)有助于防止资源耗尽,但是可能较难调整和控制。队列大小和最大池大小可能需要相互折衷:使用大型队列和小型池可以最大限度地降低 CPU 使用率、操作系统资源和上下文切换开销,但是可能导致人工降低吞吐量。如果任务频繁阻塞(例如,如果它们是 I/O 边界),则系统可能为超过您许可的更多线程安排时间。使用小型队列通常要求较大的池大小,CPU 使用率较高,但是可能遇到不可接受的调度开销,这样也会降低吞吐量。

 

handler:RejectedExecutionHandler(饱和策略),当队列和线程池都满了,说明线程池处于饱和状态,那么必须采取一种策略处理提交的新任务。这个策略默认情况下是AbortPolicy,表示无法处理新任务时抛出异常。

   1.在默认的 ThreadPoolExecutor.AbortPolicy 中,处理程序遭到拒绝将抛出运行时 RejectedExecutionException

   2.在 ThreadPoolExecutor.CallerRunsPolicy 中,线程调用运行该任务的 execute 本身。此策略提供简单的反馈控制机制,能够减缓新任务的提交速度。

   3.在 ThreadPoolExecutor.DiscardPolicy 中,不能执行的任务将被删除。

   4.在 ThreadPoolExecutor.DiscardOldestPolicy 中,如果执行程序尚未关闭,则位于工作队列头部的任务将被删除,然后重试执行程序(如果再次失败,则重复此过程)。

定义和使用其他种类的  RejectedExecutionHandler 类也是可能的,但这样做需要非常小心,尤其是当策略仅用于特定容量或排队策略时。

keepAliveTime(线程活动保持时间):线程池的工作线程空闲后,保持存活的时间。所以如果任务很多,并且每个任务执行的时间比较短,可以调大这个时间,提高线程的利用率。
unit(线程活动保持时间的单位):可选的单位有天(DAYS),小时(HOURS),分钟(MINUTES),毫秒(MILLISECONDS),微秒(MICROSECONDS, 千分之一毫秒)和毫微秒(NANOSECONDS, 千分之一微秒)。
threadFactory:用于设置创建线程的工厂,可以通过线程工厂给每个创建出来的线程设置更有意义的名字。如果没有另外说明,则在同一个  ThreadGroup 中一律使用  Executors.defaultThreadFactory() 创建线程,并且这些线程具有相同的  NORM_PRIORITY 优先级和非守护进程状态。通过提供不同的 ThreadFactory,可以改变线程的名称、线程组、优先级、守护进程状态,等等。如果从  newThread 返回 null 时  ThreadFactory 未能创建线程,则执行程序将继续运行,但不能执行任何任务。
 
钩子 (hook) 方法 
此类提供  protected 可重写的  beforeExecute(java.lang.Thread, java.lang.Runnable) 和  afterExecute(java.lang.Runnable, java.lang.Throwable) 方法,这两种方法分别在执行每个任务之前和之后调用。它们可用于操纵执行环境;例如,重新初始化 ThreadLocal、搜集统计信息或添加日志条目。此外,还可以重写方法  terminated() 来执行 Executor 完全终止后需要完成的所有特殊处理。   如果钩子 (hook) 或回调方法抛出异常,则内部辅助线程将依次失败并突然终止。队列维护   方法  getQueue() 允许出于监控和调试目的而访问工作队列。强烈反对出于其他任何目的而使用此方法。 remove(java.lang.Runnable) 和  purge() 这两种方法可用于在取消大量已排队任务时帮助进行存储回收。
 
线程池的关闭
我们可以通过调用线程池的shutdown或shutdownNow方法来关闭线程池,它们的原理是遍历线程池中的工作线程,然后逐个调用线程的interrupt方法来中断线程,所以无法响应中断的任务可能永远无法终止。但是它们存在一定的区别,shutdownNow首先将线程池的状态设置成STOP,然后尝试停止所有的正在执行或暂停任务的线程,并返回等待执行任务的列表,而shutdown只是将线程池的状态设置成SHUTDOWN状态,然后中断所有没有正在执行任务的线程。只要调用了这两个关闭方法的其中一个,isShutdown方法就会返回true。当所有的任务都已关闭后,才表示线程池关闭成功,这时调用isTerminaed方法会返回true。至于我们应该调用哪一种方法来关闭线程池,应该由提交到线程池的任务特性决定,通常调用shutdown来关闭线程池,如果任务不一定要执行完,则可以调用shutdownNow。
 
总结
举个不太优雅的例子,将线程池比喻成一个窑子,每个任务都是嫖客,线程就是接待客人的妹子,一个任务一条线程(不搞多P),corePoolSize是坐台的妹子数,maximumPoolSize是妹子的总数,任务队列是排着正在ooxx的以及准备ooxx的其大小为一次最多只允许有多少对在ooxx,keepAliveTime是每个妹子最大工作时长,做完若没超过时长就是闲着的,从闲着的坐台妹子选出再接另外的,若没闲着的坐台妹子,再叫人数过来补充,但不能超过妹子总数,超过了的话按经营策略( RejectedExecutionHandler——策略 处理, 所以可以想象,窑子老板就想多赚,坐台的妹子只要还行,接着来,不会轻易给你个新鲜的 因为有任务队列大小限制(房间有限,允许ooxx的对数不能太多),若队列满了,生意好,客人只能携着妹子等了。。。。大概意思是如此,给个图自己脑补一下:
Java线程基础——线程池使用示例_第2张图片
 
 
以下是示例代码:
ThreadPoolGenerator.java,封装了ThreadPoolExecutor的创建:
package testThreadPool;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Executors;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class ThreadPoolGenerator {

	private volatile static ThreadPoolExecutor threadPoolExecutor;
	
	//写了自己的一个Executor,主要是用于线程池执行完所有任务所需的时间,重写了terminated方法
	public static class MyThreadPoolExecutor extends ThreadPoolExecutor {

		long sTime = 0L;
		long eTime = 0L;
		
		public MyThreadPoolExecutor(int corePoolSize, int maximumPoolSize,
				long keepAliveTime, TimeUnit unit,
				BlockingQueue workQueue, ThreadFactory threadFactory,
				RejectedExecutionHandler handler) {
			super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
					threadFactory, handler);
		}
		
		@Override
		protected void terminated() {
			super.terminated();
			if(sTime != 0l) {
				eTime = System.currentTimeMillis();
				System.out.println("executor completed tasks take: " + (eTime - sTime) + " ms");
			}
			
		}
		
		//记录Executor开始执行的那刻的时间
		public void startLogTime() {
			sTime = System.currentTimeMillis();
		}
	}
	
	public static ThreadPoolExecutor getThreadPoolExecutor(int corePoolSize, int maximumPoolSize,
			Long keepAliveTime, int quequeSize, ThreadFactory threadFactory, RejectedExecutionHandler handler) {
		
		if(threadPoolExecutor == null) {
			synchronized (ThreadPoolGenerator.class) {
				if(threadPoolExecutor == null) {
					threadFactory = threadFactory == null ? Executors.defaultThreadFactory() : threadFactory;
					handler = handler == null ? new ThreadPoolExecutor.AbortPolicy() : handler;
					
					threadPoolExecutor = new MyThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime,
							TimeUnit.MILLISECONDS, new ArrayBlockingQueue(quequeSize), threadFactory, handler);
				}
			}
		}
		
		return threadPoolExecutor;
	}
}
TestTask.java, 一个测试任务类:
package testThreadPool;

public class TestTask implements Runnable{

	private String name;
	
	public TestTask(String name) {
		this.name = name;
	}
	
	
	//模拟任务,此处两个for 循环,若循环次数不大,那么有线程执行跟没线程执行效果差不多的
	public void performTask() {
		for(int i=0; i<100000000; i++){
			for(int j=0; j<1000000; j++) {
				j=i+j;
			}
		}
		
		System.out.println(name + " completed.");
	}

	//通过线程执行模拟任务
	@Override
	public void run() {
		this.performTask();
		
	}
}
 
TestCase.java,测试类:
package testThreadPool;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ThreadPoolExecutor;

import testThreadPool.ThreadPoolGenerator.MyThreadPoolExecutor;


public class TestCase {

	
	public static void main(String[] args) {
		
		List tasks = new ArrayList();
		for(int i=1; i<=5; i++) 
			tasks.add(new TestTask("Task " + i));
		
		
		TestCase c = new TestCase();
		
		System.out.println("no thread case:");
		
		long sTime = System.currentTimeMillis();
		
		c.noThreadProcess(tasks);
		
		long eTime = System.currentTimeMillis();
		long tTime = eTime - sTime;
		System.out.println("process take :" + tTime + "ms");
		
		System.out.println("=========================");

		
		System.out.println();
		
		
		System.out.println("with thread case:");
		
		ThreadPoolExecutor executor = ThreadPoolGenerator.getThreadPoolExecutor(3, 5, 5000L, 3, null, null);
		
		c.withThreadProcess(executor, tasks);
		
		eTime = System.currentTimeMillis();
		
		System.out.println("=========================");
		
	}
	
	
	
	public void withThreadProcess(ThreadPoolExecutor executor, List list) {
		
		//此举用来启动记录执行耗时
		((MyThreadPoolExecutor)executor).startLogTime();
		
		for(TestTask task : list) {
			executor.execute(task);
		}
		executor.shutdown();
		
	}
	
	public void noThreadProcess(List list) {
		
		for(TestTask task : list) {
			task.performTask();
		}
		
	}

}
 测试结果:
no thread case:
Task 1 completed.
Task 2 completed.
Task 3 completed.
Task 4 completed.
Task 5 completed.
process take :2802ms
=========================

with thread case:
=========================
Task 3 completed.
Task 2 completed.
Task 1 completed.
Task 4 completed.
Task 5 completed.
executor completed tasks take: 1224 ms
 
 
 线程池另外一种用法,用线程池执行任务,完成后的取得执行结果:
TestMessageTask.java,那个badTask如果为true,表示为坏任务,模拟任务执行出错:
package testThreadPool;

import java.util.concurrent.Callable;

public class TestMessageTask implements Callable {

	private String name;
	private boolean badTask;
	
	public TestMessageTask(String name, boolean badTask) {
		this.name = name;
		this.badTask = badTask;
	}
	
	@Override
	public String call() throws Exception {
		for(int i=0; i<100000000; i++){
			for(int j=0; j<1000000; j++) {
				j=i+j;
			}
		}
		
		//用了一个boolean,模拟执行出错的情形
		if(badTask) {
			throw new RuntimeException(name + " is bad task!!");
		}
		return name + " completed.";
	}

}
 
MessageThreadPoolExecutor.java:
package testThreadPool;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Callable;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.RunnableFuture;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class MessageThreadPoolExecutor extends ThreadPoolExecutor{

	
	//把父类的构造函数全弄出来算了。。。
	public MessageThreadPoolExecutor(int corePoolSize, int maximumPoolSize,
			long keepAliveTime, TimeUnit unit,
			BlockingQueue workQueue, RejectedExecutionHandler handler) {
		super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, handler);
	}

	public MessageThreadPoolExecutor(int corePoolSize, int maximumPoolSize,
			long keepAliveTime, TimeUnit unit,
			BlockingQueue workQueue, ThreadFactory threadFactory) {
		super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
				threadFactory);
	}

	public MessageThreadPoolExecutor(int corePoolSize, int maximumPoolSize,
			long keepAliveTime, TimeUnit unit, BlockingQueue workQueue) {
		super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
	}

	public MessageThreadPoolExecutor(int corePoolSize, int maximumPoolSize,
			long keepAliveTime, TimeUnit unit,
			BlockingQueue workQueue, ThreadFactory threadFactory,
			RejectedExecutionHandler handler) {
		
		super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
				threadFactory, handler);
	}
	
	
	//定义了一个方法执行任务集合,用了泛型,表示结果类型由Callable类型的任务对象决定
	public  Map performTasks(Map> taskMap) throws InterruptedException{
		
		if(taskMap == null || taskMap.isEmpty()) 
			throw new NullPointerException();
		
		Map> futureMap = new HashMap>();
		Map messageMap = new HashMap();
		
		boolean done = false;
		
		try {
			
			for(String key : taskMap.keySet()) {
				/**
				 * 这里用了两种方式执行任务:execute与submit,建议翻翻API文档,
				 * 关于Future的get方法,没弄懂。。。
				 */
				//execute 方式
				/*RunnableFuture futureTask = new FutureTask(taskMap.get(key));
				execute(futureTask);
				futureMap.put(key, futureTask);*/
				
				
				//submit 方式
				futureMap.put(key, submit(taskMap.get(key)));
				
			}
			
			/**
			 *  再次遍历任务,逐个调用get方法,get方法会阻塞住直到任务完成,
			 *  get方法返回一个结果,根据结果判断任务执行成功与否,这也是我没有看懂
			 *  API的地方,那个submit方法明明说返回的Future对象如果成功它的get方法
			 *  返回null,但messageMap中的value是有的,不为null。。。 
			 */
			for(String key : futureMap.keySet()) {
				Future f = futureMap.get(key);
				try {
					T result = f.get();
					messageMap.put(key, result);
				} catch (ExecutionException e) {
					System.out.println(e.getMessage());
				}
			}
			
			done = true;
			return messageMap;
		}finally {
			//若上面出了异常没done,没做完的任务直接cancel
			if(!done) {
				for(String key : futureMap.keySet()) {
					futureMap.get(key).cancel(true);
				}
			}
			
			this.shutdown();
		}
		
	}
	

}
 
测试代码,MsgTestCase.java:
package testThreadPool;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;

public class MsgTestCase {

	public static void main(String[] args) {
		
		Map>taskMap = new HashMap>();
		for(int i=1; i<=5; i++) {
			
			//随机产生坏任务
			int r = (int)(Math.random()*5 + 1);
			boolean badTask = (i==r);
			
			taskMap.put("Task: " + i ,new TestMessageTask("Task: " + i, badTask));
		}
		
		
		MessageThreadPoolExecutor executor = new MessageThreadPoolExecutor(3, 5, 3000L, 
					TimeUnit.MILLISECONDS, 
					new ArrayBlockingQueue(2));
		
		try {
			Map resultMap = executor.performTasks(taskMap);
			
			for(String key : resultMap.keySet()) {
				System.out.println(resultMap.get(key));
			}
		} catch (InterruptedException e) {
			e.printStackTrace();
		} 
		
	}

}
 测试结果:
java.lang.RuntimeException: Task: 5 is bad task!!
java.lang.RuntimeException: Task: 2 is bad task!!
Task: 3 completed.
Task: 4 completed.
Task: 1 completed.
 
 
  • Java线程基础——线程池使用示例_第3张图片
  • 大小: 64.4 KB
  • Java线程基础——线程池使用示例_第4张图片
  • 大小: 13.2 KB
  • 查看图片附件

你可能感兴趣的:(java,线程池,简介,示例)