引言
合理利用线程池能够带来三个好处:
第一:降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。第二:提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。第三:提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。
本篇参考: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,饱和策略)处理。
参考图:
任务队列又有以下集中策略模式:
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(线程活动保持时间):线程池的工作线程空闲后,保持存活的时间。所以如果任务很多,并且每个任务执行的时间比较短,可以调大这个时间,提高线程的利用率。
ThreadGroup
中一律使用
Executors.defaultThreadFactory()
创建线程,并且这些线程具有相同的
NORM_PRIORITY 优先级和非守护进程状态。通过提供不同的 ThreadFactory,可以改变线程的名称、线程组、优先级、守护进程状态,等等。如果从
newThread 返回 null 时
ThreadFactory 未能创建线程,则执行程序将继续运行,但不能执行任何任务。
beforeExecute(java.lang.Thread, java.lang.Runnable)
和
afterExecute(java.lang.Runnable, java.lang.Throwable)
方法,这两种方法分别在执行每个任务之前和之后调用。它们可用于操纵执行环境;例如,重新初始化 ThreadLocal、搜集统计信息或添加日志条目。此外,还可以重写方法
terminated()
来执行 Executor 完全终止后需要完成的所有特殊处理。 如果钩子 (hook) 或回调方法抛出异常,则内部辅助线程将依次失败并突然终止。队列维护 方法
getQueue()
允许出于监控和调试目的而访问工作队列。强烈反对出于其他任何目的而使用此方法。
remove(java.lang.Runnable)
和
purge()
这两种方法可用于在取消大量已排队任务时帮助进行存储回收。
- 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<Runnable> 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<Runnable>(quequeSize), threadFactory, handler);
- }
- }
- }
- return threadPoolExecutor;
- }
- }
- 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();
- }
- }
- 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<TestTask> tasks = new ArrayList<TestTask>();
- 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<TestTask> list) {
- //此举用来启动记录执行耗时
- ((MyThreadPoolExecutor)executor).startLogTime();
- for(TestTask task : list) {
- executor.execute(task);
- }
- executor.shutdown();
- }
- public void noThreadProcess(List<TestTask> 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
- package testThreadPool;
- import java.util.concurrent.Callable;
- public class TestMessageTask implements Callable<String> {
- 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.";
- }
- }
- 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<Runnable> workQueue, RejectedExecutionHandler handler) {
- super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, handler);
- }
- public MessageThreadPoolExecutor(int corePoolSize, int maximumPoolSize,
- long keepAliveTime, TimeUnit unit,
- BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory) {
- super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
- threadFactory);
- }
- public MessageThreadPoolExecutor(int corePoolSize, int maximumPoolSize,
- long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {
- super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
- }
- public MessageThreadPoolExecutor(int corePoolSize, int maximumPoolSize,
- long keepAliveTime, TimeUnit unit,
- BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory,
- RejectedExecutionHandler handler) {
- super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
- threadFactory, handler);
- }
- //定义了一个方法执行任务集合,用了泛型,表示结果类型由Callable类型的任务对象决定
- public <T> Map<String, T> performTasks(Map<String, Callable<T>> taskMap) throws InterruptedException{
- if(taskMap == null || taskMap.isEmpty())
- throw new NullPointerException();
- Map<String, Future<T>> futureMap = new HashMap<String, Future<T>>();
- Map<String, T> messageMap = new HashMap<String, T>();
- boolean done = false;
- try {
- for(String key : taskMap.keySet()) {
- /**
- * 这里用了两种方式执行任务:execute与submit,建议翻翻API文档,
- * 关于Future的get方法,没弄懂。。。
- */
- //execute 方式
- /*RunnableFuture<T> futureTask = new FutureTask<T>(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<T> 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();
- }
- }
- }
- 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<String, Callable<String>>taskMap = new HashMap<String, Callable<String>>();
- 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<Runnable>(2));
- try {
- Map<String, String> 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.