- 浏览: 280376 次
- 性别:
- 来自: 上海
文章分类
最新评论
-
haiyangyiba:
jimichan 写道果然3.1.0.M2不行,切换到 3.1 ...
spring 使用注解装配的Bean如何使用property-placeholder属性配置中的值 -
lishl:
非常不错。用心之作。谢谢。
Jetty6 指南书 第4章 Jetty服务配置文件 -
Yinny:
楼主V5 网上的文章一搜一堆互相copy的,自己写的文章 太难 ...
Jetty6 指南和解析 - 第二章 Jetty初探 -
290845534:
wangpeihu 写道我想收藏,可是我不敢,我没有那500W ...
Jetty6 指南和解析 - 第二章 Jetty初探 -
宋建勇:
对于我们这些菜鸟来说,你突来停稿了,很伤脑筋啊,希望楼主继续! ...
时间飞快,jetty7已经发展成熟
第一部分 集合 http://jimichan.iteye.com/blog/951948
第二部分 线程池 http://jimichan.iteye.com/blog/951950
第三部分 锁 http://jimichan.iteye.com/blog/951954
第四部分 同步辅助类 http://jimichan.iteye.com/blog/951955
Concurrent In Java,第二部分 线程池
2011-3-9 延昭 & 陈汝烨 版权所有,特别禁止发布到百度文库
这篇是来自公司内部分享会议是写的总结,有些内容没有表达出来,大家可以来踩,但是需留下原因,以便后续补充。
第一部分 集合 http://jimichan.iteye.com/blog/951948
第二部分 线程池 http://jimichan.iteye.com/blog/951950
第三部分 锁 http://jimichan.iteye.com/blog/951954
第四部分 同步辅助类 http://jimichan.iteye.com/blog/951955
2. ThreadPool
虽然线程和进程相比是轻量级许多,但是线程的创建成本还是不可忽律,所以就有了线程池化的设计。线程池的创建、管理、回收、任务队列管理、任务分配等细节问题依然负责,没有必要重复发明轮子,concurrent包已经为我们准备了一些优秀线程池的实现。
2.1 认识ExecutorService 接口
ExecutorService 接口,它能提供的功能就是用来在将来某一个时刻异步地执行一系列任务。虽然简单一句话,但是包含了很多需求点。它的实现至少包含了线程池和任务队列两个方面,其实还包括了任务失败处理策略等。
经常使用submit方法,用来提交任务对象。
简单的例子:
ExecutorService es = Executors.newCachedThreadPool();
es.submit(new Runnable(){ @Override public void run() { System.out.println("do some thing"); } });
es.shutdown();
|
上面的例子只是完成了提交了一个任务,异步地去执行它。但是有些使用场景更为复杂,比如等待获得异步任务的返回结果,或者最多等上固定的时间。
submit 方法返回一个对象,Future。看起来有点别扭,代表将来的对象。其实看一下Future的方法就明白了。
其实Future对象代表了一个异步任务的结果,可以用来取消任务、查询任务状态,还有通过get方法获得异步任务返回的结果。当调用get方法的时候,当前线程被阻塞直到任务被处理完成或者出现异常。
我们可以通过保存Future对象来跟踪查询异步任务的执行情况。
显然Runnable接口中定义的 public void run();方法并不能返回结果对象,所以concurrent包提供了Callable接口,它可以被用来返回结果对象。
2.2 ThreadPoolExecutor
ThreadPoolExecutor实现了ExecutorService 接口,也是我们最主要使用的实现类。
首先非常有必要看一些类的最完整的构造函数
ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
ThreadPoolExecutor对象中有个poolSize变量表示当前线程池中正在运行的线程数量。
注意:这个有关非常重要的关系,常常被误解。poolSize变量和corePoolSize、maximumPoolSize以及workQueue的关系。
首先线程池被创建初期,还没有执行任何任务的时候,poolSize等于0;
每次向线程池提交任务的时候,线程池处理过程如下:
1. 如果poolSize少于 corePoolSize,则首选添加新的线程,而不进行排队。
2. 如果poolSize等于或多于 corePoolSize,则首选将请求加入队列workQueue,而不添加新的线程。
3. 如果第二步执行失败(队已满),则创建新的线程执行任务,但是如果果poolSize已经达到maximumPoolSize,那么就拒绝该任务。如果处理被拒绝的任务就取决于RejectedExecutionHandler handler的设置了,默认情况下会抛出异常。
系统存在四种任务拒绝策略:
- 在默认的 ThreadPoolExecutor.AbortPolicy 中,处理程序遭到拒绝将抛出运行时 RejectedExecutionException。
- 在 ThreadPoolExecutor.CallerRunsPolicy 中,线程调用运行该任务的 execute 本身。此策略提供简单的反馈控制机制,能够减缓新任务的提交速度。
- 在 ThreadPoolExecutor.DiscardPolicy 中,不能执行的任务将被删除。
- 在 ThreadPoolExecutor.DiscardOldestPolicy 中,如果执行程序尚未关闭,则位于工作队列头部的任务将被删除,然后重试执行程序(如果再次失败,则重复此过程)。
keepAliveTime活动线程如果空闲一段时间是否可以回收,通常只作用于超出corePoolSize的线程。corePoolSize的线程创建了就不会被回收。但是到java 6 之后增加了public void allowCoreThreadTimeOut(boolean value)方法,允许core进程也可以根据keepAliveTime来回收,默认为false。
决定线程池特性的还有workQueue的实现类,有三种类SynchronousQueue、LinkedBlockingQueue、ArrayBlockingQueue,分别对应同步队列、无界队列、有界队列。
(摘自JavaDoc)
- 类SynchronousQueue,直接提交。工作队列的默认选项是 SynchronousQueue,它将任务直接提交给线程而不保持它们。在此,如果不存在可用于立即运行任务的线程,则试图把任务加入队列将失败,因此会构造一个新的线程。此策略可以避免在处理可能具有内部依赖性的请求集时出现锁。直接提交通常要求无界 maximumPoolSizes 以避免拒绝新提交的任务(设置maximumPoolSizes 为Integer.MAX_VALUE)。当命令以超过队列所能处理的平均数连续到达时,此策略允许无界线程具有增长的可能性。
- LinkedBlockingQueue,无界队列。使用无界队列(例如,不具有预定义容量的 LinkedBlockingQueue)将导致在所有 corePoolSize 线程都忙时新任务在队列中等待。这样,创建的线程就不会超过 corePoolSize。(因此,maximumPoolSize 的值也就无效了。)当每个任务完全独立于其他任务,即任务执行互不影响时,适合于使用无界队列;例如,在 Web 页服务器中。这种排队可用于处理瞬态突发请求,当命令以超过队列所能处理的平均数连续到达时,此策略允许无界线程具有增长的可能性。
- ArrayBlockingQueue,有界队列。当使用有限的 maximumPoolSizes 时,有界队列(如 ArrayBlockingQueue)有助于防止资源耗尽,但是可能较难调整和控制。队列大小和最大池大小可能需要相互折衷:使用大型队列和小型池可以最大限度地降低 CPU 使用率、操作系统资源和上下文切换开销,但是可能导致人工降低吞吐量。如果任务频繁阻塞(例如,如果它们是 I/O 边界),则系统可能为超过您许可的更多线程安排时间。使用小型队列通常要求较大的池大小,CPU 使用率较高,但是可能遇到不可接受的调度开销,这样也会降低吞吐量。
综上:构造参数的设置是互相制约和影响的。只有当你重复了解其相互关系的时候、或有特殊需求的时候,才可以自己构造ThreadPoolExecutor对象,否则可以使用Executores是个工厂类。
提示使用线程池是注意处理shutdown,确保你系统关闭的时候主动关闭shutdown。
2.3 ScheduledExecutorService
扩展了ExecutorService接口,提供时间排程的功能。
schedule(Callable<V> callable, long delay, TimeUnit unit) 创建并执行在给定延迟后启用的 ScheduledFuture。 |
schedule(Runnable command, long delay, TimeUnit unit) 创建并执行在给定延迟后启用的一次性操作。 |
scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnitunit) 创建并执行一个在给定初始延迟后首次启用的定期操作,后续操作具有给定的周期;也就是将在 initialDelay 后开始执行,然后在initialDelay+period 后执行,接着在 initialDelay + 2 * period 后执行,依此类推。 |
scheduleWithFixedDelay(Runnable command, long initialDelay, long delay,TimeUnit unit) 创建并执行一个在给定初始延迟后首次启用的定期操作,随后,在每一次执行终止和下一次执行开始之间都存在给定的延迟。
|
schedule方法被用来延迟指定时间来执行某个指定任务。如果你需要周期性重复执行定时任务可以使用scheduleAtFixedRate或者scheduleWithFixedDelay方法,它们不同的是前者以固定频率执行,后者以相对固定频率执行。
(感谢wenbois2000 提出原先的错误,我在这里重新描述!对于原先的错误,实在不好意思啊,再次感谢!)
不管任务执行耗时是否大于间隔时间,scheduleAtFixedRate和scheduleWithFixedDelay都不会导致同一个任务并发地被执行。唯一不同的是scheduleWithFixedDelay是当前一个任务结束的时刻,开始结算间隔时间,如0秒开始执行第一次任务,任务耗时5秒,任务间隔时间3秒,那么第二次任务执行的时间是在第8秒开始。
ScheduledExecutorService的实现类,是ScheduledThreadPoolExecutor。ScheduledThreadPoolExecutor对象包含的线程数量是没有可伸缩性的,只会有固定数量的线程。不过你可以通过其构造函数来设定线程的优先级,来降低定时任务线程的系统占用。
特别提示:通过ScheduledExecutorService执行的周期任务,如果任务执行过程中抛出了异常,那么过ScheduledExecutorService就会停止执行任务,且也不会再周期地执行该任务了。所以你如果想保住任务都一直被周期执行,那么catch一切可能的异常。
2.4 Executors
Executores是个工厂类,用来生成ThreadPoolExecutor对象,它提供了一些常用的线程池配置方案,满足我们大部分场景。
1. newCachedThreadPool
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
分析下出这个线程池配置的工作模式,当没有空闲进程时就新建线程执行,当有空闲线程时就使用空闲线程执行。当线程空闲大60秒时,系统自动回收线程。
该线程池非常适合执行短小异步任务时吞吐量非常高,会重复利用CPU的能力。但是如果任务处理IO边界任务,那么会消耗大量线程切换,降低系统吞吐量。所以执行短小的计算任务非常高效,且当没有任务时不会消耗系统资源。
注意:线程池中没有变量表示线程是否空闲。那么程序是如何控制的呢?不得不赞叹concurrent实现的非常精巧。当创建出来的线程完成原来的任务后,会调用BlockingQueue的Poll方法,对于SynchronousQueue实现而言会阻塞调用线程,直到另外的线程offer调用。
然而ThreadPool在分配任务的时候总是先去尝试调用offer方法,所以就会触发空闲线程再次调用。
精妙的是ThreadPoolExecutor的处理逻辑一样,但是用BlockingQueue实现变了就产生不同的行为。
2. newFixedThreadPool
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
创建固定线程数量的线程池,采用无界队列,当有更多任务的时候将被放入工作队列中排队。如果线程池不经常执行任务时,你可以调用allowCoreThreadTimeOut(boolean value)的方法让系统自动回收core的进程,以节约系统资源。
3. newSingleThreadExecutor
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
只有一个工作线程的线程池。和newFixedThreadPool(1)相比,不同之处有两点:
1. 不可以重新配置newSingleThreadExecutor创建出来的线程池。
2. 当创建出来的线程池对象被GC回收时,会自动调用shutdown方法。
4.newScheduledThreadPool
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
生成一个可以执行时间调度的线程池。其实内部使用无界工作队列,线程数量最多能达到corePoolSize。
2.5 ExecutorCompletionService
这是个巧妙的设计,内部维护了一已经完成了任务结果队列,通过take方法可以同步地等待一个个结果对象。
第三部分 LOCK
http://jimichan.iteye.com/blog/951954
评论
我试验的结果是在使用scheduleAtFixedRate 时,任务在上次执行未完成是不会继续开始的,并且在ScheduledExecutorService.scheduleAtFixedRate的Javadoc 上也明确注释了这一点:
If any execution of this task takes longer than its period, then subsequent executions may start late, but will not concurrently execute.
下面是完整的JavaDoc
/** * Creates and executes a periodic action that becomes enabled first * after the given initial delay, and subsequently with the given * period; that is executions will commence after * <tt>initialDelay</tt> then <tt>initialDelay+period</tt>, then * <tt>initialDelay + 2 * period</tt>, and so on. * If any execution of the task * encounters an exception, subsequent executions are suppressed. * Otherwise, the task will only terminate via cancellation or * termination of the executor. If any execution of this task * takes longer than its period, then subsequent executions * may start late, but will not concurrently execute. * * @param command the task to execute * @param initialDelay the time to delay first execution * @param period the period between successive executions * @param unit the time unit of the initialDelay and period parameters * @return a ScheduledFuture representing pending completion of * the task, and whose <tt>get()</tt> method will throw an * exception upon cancellation * @throws RejectedExecutionException if the task cannot be * scheduled for execution * @throws NullPointerException if command is null * @throws IllegalArgumentException if period less than or equal to zero */ public ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit);
发表评论
-
spring 使用注解装配的Bean如何使用property-placeholder属性配置中的值
2012-03-13 14:03 13339很久没动笔了 spring 使用注解装配的Bean如何 ... -
轻松配置log4j,实现错误消息的Gtalk通知消息到智能手机。
2011-07-06 18:11 2007在Android手机上装个Gtalk,接收Log4j的异常提醒 ... -
系统URL规划
2011-06-13 12:18 0一个好的系统URL规划,不仅可以使得URL美观、简单易懂,而且 ... -
干掉讨厌的commons-logging依赖
2011-06-13 11:42 6222因为使用Slf4j,所以一直以来都对commons-loggi ... -
Concurrent In Java 6 分享,第四部分 同步辅助类介绍
2011-03-10 11:46 4218第一部分 集合 http://jimich ... -
Concurrent In Java 6 分享,第三部分 锁
2011-03-10 11:45 3936第一部分 集合 http://jimich ... -
Concurrent In Java 6 分享,第一部分 集合
2011-03-10 11:43 6164第一部分 集合 http://jimichan.i ... -
Concurrent In Java 6 分享 你不一定都了解
2011-03-10 11:27 2157由于帖子字数限制无法完整发布,请访问博客吧 第一 ... -
spring 2.5 中文chm文档
2010-07-22 16:00 4242换了个电脑,想看下spring 2.5 的中文chm文档。 ... -
正确使用ThreadPoolExecutor
2010-02-04 10:23 3011一直都是使用Executors.new ... -
jetty自带的服务端代理(proxy)
2009-11-02 13:33 6971代理配置的文档 http://docs.codehaus.or ... -
在eclipse中 配置spring 自定义的schema文件
2009-04-08 16:24 6909问题:使用了spring自定义schema时,在xml文件中无 ... -
spring jdbc的批处理功能 ---- 当中途失败时
2009-03-19 16:12 2647spring JdbcTemplate有一个功能是batchU ... -
搜索java类路径中文件的方法
2009-03-10 11:10 1915问题: 搜索所有类路径中所有符合文件名规则的资源? 解决方 ... -
spring事务管理在mysql数据库无法回滚
2008-11-07 11:31 3543检查了半天,配置和程序都没有问题,就是无法回滚数据。 最后才 ... -
oracle jdbc链接异常解决一则
2008-09-09 14:03 1655在linux 下连接另外一个oracle数据库出错了 异常: ... -
用java好久,今天才知道初始化集合还可以这样写
2008-05-06 16:53 14816Set set = new HashSet() {{ ... -
请教关于使用PropertyOverrideConfigurer,怎么覆写 bean引用的问题
2008-01-07 16:46 1588大家都知道使用PropertyOverrideConfigur ... -
理解javascript的类构造函数
2007-09-05 13:13 3110我们经常使用new运行符去create ... -
Struts2 Wildcard Mappings用法之访问tiles定义
2007-08-23 16:19 2528哈哈,终于可以用struts2了。以前没用过webwork,几 ...
相关推荐
【自定义Java线程池实现】 在Java编程中,线程池是一种高效管理线程资源的方式,可以提高系统的性能和响应速度。...如《Effective Java》第二版第47条建议,避免重复造轮子,使用成熟且经过优化的类库。
《Concurrent Programming in Java》第二版是一本非常有价值的参考书,适合希望深入了解Java并发编程的开发人员。通过本书的学习,读者不仅可以掌握Java提供的强大并发工具,还能学会如何设计健壮、高效的并发程序。...
《Java并发编程:设计原则与模式》是一本深入探讨Java多线程编程的权威书籍,由Doug Lea撰写,第二版全面涵盖了Java并发处理的各个方面。这本书不仅提供了丰富的理论知识,还介绍了实战中的设计原则和模式,对于Java...
# 并发编程在Java中的应用:设计原则与模式(第二版) ## 一、并发对象导向编程概览 ### 1.1 引言 本书《Concurrent Programming in Java - Design Principles and Patterns (Second Edition)》深入探讨了在Java...
最后,"Addison_Wesley_-_Concurrent_Programming_in_Java_2nd_Ed_(1999).pdf"是《并发编程在Java》的第二版,由Doug Lea撰写。这本书是Java并发编程领域的另一部里程碑作品,它在Java 1.4时代就深入介绍了线程、...
《并发编程在Java》是Doug Lea的经典著作,第二版详细阐述了Java平台上的并发设计原则和模式。这本书深入探讨了如何有效地编写多线程和并发应用程序,是Java程序员理解和利用并发的重要参考资料。 并发编程是现代...
Java线程池是Java并发编程中的重要组成部分,它在多线程编程中扮演着至关重要的角色,有效地管理和调度线程资源,提高了程序的性能和稳定性。本文将深入探讨Java线程池的概念、工作原理以及如何在实际开发中运用。 ...
项目描述:对java.util.concurrent包下线程池相关源码进行重新实现,深入研究和学习线程池超时机制、饱和策略、生命周期等知识 ThreadPoolExecutor类下部分方法和内部类介绍: 1、Worker类: 描述:Worker类实现...
Java中的线程池是由`java.util.concurrent`包中的`ExecutorService`接口及其实现类如`ThreadPoolExecutor`提供的。线程池可以有效地管理和控制并发执行的任务数量,避免频繁创建和销毁线程带来的性能开销。通过设置...
#### 二、`java.util.concurrent`包的关键概念与组件 ##### 2.1 Executor框架 `Executor`框架是`java.util.concurrent`的核心组件之一,它为任务的执行提供了一个统一的接口。其中最重要的接口是`ExecutorService`...
19 适用于 Java 程序员的 CSP ,第 2 部分.mht 20 适用于 Java 程序员的 CSP ,第 3 部分.mht 21 实现一个不受约束的不变性模型.mht 22 实时 Java,第 3 部分 线程化和同步.mht 23 IBM 的 Java 诊断,第 3 部分 ...
Java concurrency线程池之线程池原理 Java concurrency线程池是一种高效的线程池实现,用于管理和执行多个线程任务。线程池原理是Java concurrency线程池的核心机制,用于管理线程池中的线程资源。下面将详细介绍...
在Java编程领域,`java.util.concurrent`包是并发编程的核心工具包,提供了高效、线程安全的类和接口,使得开发者能够更容易地处理多线程环境。本篇将深入探讨这个包中一些鲜为人知的知识点,以帮助你提升并发编程的...
Java线程池是Java并发编程中的重要组成部分,它在多线程编程中扮演着至关重要的角色,有效地管理和调度线程资源,提高系统性能并降低资源消耗。本资料"JavaThreaddemo_DEMO_tidecme_线程池Java_源码.zip"包含了关于...
在Java中,`java.util.concurrent`包提供了`ExecutorService`接口和它的实现类,如`ThreadPoolExecutor`,用于创建线程池。`ThreadPoolExecutor`构造函数接收几个关键参数:核心线程数、最大线程数、存活时间、时间...
《实战Java高并发程序设计》第二版是一本深入探讨Java多线程和并发编程的书籍。这本书涵盖了Java并发编程的核心概念和技术,旨在帮助开发者在实际项目中高效地处理高并发场景。随书附带的代码提供了丰富的示例,以便...
Java 1.6增强了并发编程的支持,提供了`java.util.concurrent`包,包含线程池、Future、Callable接口以及并发集合类,如ConcurrentHashMap、CopyOnWriteArrayList等,这些使得多线程编程更加高效和安全。...
在Android中,我们可以使用`java.util.concurrent`包下的`ExecutorService`和`ThreadPoolExecutor`来创建线程池。`ExecutorService`是线程池的接口,而`ThreadPoolExecutor`是它的具体实现,提供了更灵活的配置选项...
在Java中,为了提高程序的并发处理能力,Java标准库提供了多个线程安全的并发容器,它们主要位于`java.util.concurrent`包中。这些容器能够有效地管理共享资源的访问,并确保在多线程环境下数据的一致性和完整性。 ...