在多线程大师Doug Lea的贡献下,在JDK1.5中加入了许多对并发特性的支持,例如:线程池。
一、简介
线程池类为 java.util.concurrent.ThreadPoolExecutor,常用构造方法为:
ThreadPoolExecutor(int
corePoolSize,
int
maximumPoolSize,
long
keepAliveTime, TimeUnit unit,
BlockingQueue workQueue,
RejectedExecutionHandler handler)
corePoolSize: 线程池维护线程的最少数量,
maximumPoolSize:线程池维护线程的最大数量
keepAliveTime: 线程池维护线程所允许的空闲时间
unit: 线程池维护线程所允许的空闲时间的单位
workQueue: 线程池所使用的缓冲队列
handler: 线程池对拒绝任务的处理策略
一个任务通过 execute(Runnable)方法被添加到线程池,任务就是一个 Runnable类型的对象,任务的执行方法就是 Runnable类型对象的run()方法。
当一个任务通过execute(Runnable)方法欲添加到线程池时:
如果此时线程池中的数量小于corePoolSize,即使线程池中的线程都处于空闲状态,也要创建新的线程来处理被添加的任务。
如果此时线程池中的数量等于 corePoolSize,但是缓冲队列 workQueue未满,那么任务被放入缓冲队列。
如果此时线程池中的数量大于corePoolSize,缓冲队列workQueue满,并且线程池中的数量小于maximumPoolSize,建新的线程来处理被添加的任务。
如果此时线程池中的数量大于corePoolSize,缓冲队列workQueue满,并且线程池中的数量等于maximumPoolSize,那么通过 handler所指定的策略来处理此任务。
也就是:处理任务的优先级为:
核心线程corePoolSize、任务队列workQueue、最大线程maximumPoolSize,如果三者都满了,使用handler处理被拒绝的任务。
当线程池中的线程数量大于 corePoolSize时,如果某线程空闲时间超过keepAliveTime,线程将被终止。这样,线程池可以动态的调整池中的线程数。
unit可选的参数为java.util.concurrent.TimeUnit中的几个静态属性:
NANOSECONDS、MICROSECONDS、MILLISECONDS、SECONDS。
workQueue我常用的是:java.util.concurrent.ArrayBlockingQueue
handler有四个选择:
ThreadPoolExecutor.AbortPolicy()
抛出java.util.concurrent.RejectedExecutionException异常
ThreadPoolExecutor.CallerRunsPolicy()
重试添加当前的任务,他会自动重复调用execute()方法
ThreadPoolExecutor.DiscardOldestPolicy()
抛弃旧的任务
ThreadPoolExecutor.DiscardPolicy()
抛弃当前的任务
import
java.io.Serializable;
import
java.util.concurrent.ArrayBlockingQueue;
import
java.util.concurrent.ThreadPoolExecutor;
import
java.util.concurrent.TimeUnit;
public
class
TestThreadPool {
private
static
int
produceTaskSleepTime
=
2
;
private
static
int
consumeTaskSleepTime
=
2000
;
private
static
int
produceTaskMaxNumber
=
10
;
public
static
void
main(String[] args) {
//
构造一个线程池
ThreadPoolExecutor threadPool
=
new
ThreadPoolExecutor(
2
,
4
,
3
,
TimeUnit.SECONDS,
new
ArrayBlockingQueue(
3
),
new
ThreadPoolExecutor.DiscardOldestPolicy());
for
(
int
i
=
1
;i
<=
produceTaskMaxNumber;i
++
){
try
{
//
产生一个任务,并将其加入到线程池
String task
=
"
task@
"
+
i;
System.out.println(
"
put
"
+
task);
threadPool.execute(
new
ThreadPoolTask(task));
//
便于观察,等待一段时间
Thread.sleep(produceTaskSleepTime);
}
catch
(Exception e) {
e.printStackTrace();
}
}
}
/**
* 线程池执行的任务
*
@author
hdpan
*/
public
static
class
ThreadPoolTask
implements
Runnable,Serializable{
private
static
final
long
serialVersionUID
=
0
;
//
保存任务所需要的数据
private
Object threadPoolTaskData;
ThreadPoolTask(Object tasks){
this
.threadPoolTaskData
=
tasks;
}
public
void
run(){
//
处理一个任务,这里的处理方式太简单了,仅仅是一个打印语句
System.out.println(
"
start ..
"
+
threadPoolTaskData);
try
{
////
便于观察,等待一段时间
Thread.sleep(consumeTaskSleepTime);
}
catch
(Exception e) {
e.printStackTrace();
}
threadPoolTaskData
=
null
;
}
public
Object getTask(){
return
this
.threadPoolTaskData;
}
}
}
说明:
1、在这段程序中,一个任务就是一个Runnable类型的对象,也就是一个ThreadPoolTask类型的对象。
2、一般来说任务除了处理方式外,还需要处理的数据,处理的数据通过构造方法传给任务。
3、在这段程序中,main()方法相当于一个残忍的领导,他派发出许多任务,丢给一个叫 threadPool的任劳任怨的小组来做。
这个小组里面队员至少有两个,如果他们两个忙不过来, 任务就被放到任务列表里面。
如果积压的任务过多,多到任务列表都装不下(超过3个)的时候,就雇佣新的队员来帮忙。但是基于成本的考虑,不能雇佣太多的队员, 至多只能雇佣 4个。
如果四个队员都在忙时,再有新的任务, 这个小组就处理不了了,任务就会被通过一种策略来处理,我们的处理方式是不停的派发, 直到接受这个任务为止(更残忍!呵呵)。
因为队员工作是需要成本的,如果工作很闲,闲到 3SECONDS都没有新的任务了,那么有的队员就会被解雇了,但是,为了小组的正常运转,即使工作再闲,小组的队员也不能少于两个。
4、通过调整 produceTaskSleepTime和 consumeTaskSleepTime的大小来实现对派发任务和处理任务的速度的控制, 改变这两个值就可以观察不同速率下程序的工作情况。
5、通过调整4中所指的数据,再加上调整任务丢弃策略, 换上其他三种策略,就可以看出不同策略下的不同处理方式。
6、对于其他的使用方法,参看jdk的帮助,很容易理解和使用。
发邮件示例:
在普通的web应用中,发送邮件应该只能算小任务,而使用jms来发送邮件有
点杀鸡用牛刀的味道,那么如果能建立一个线程池来管理这些小线程并重复使用他们,应该来说是一个简单有效的方案,我们可以使用concurrent包中的
Executors来建立线程池,Executors是一个工厂,也是一个工具类,我把它的api的介绍简单的翻译了一下(如果翻译有误请大家不要吝啬手
中的砖头)
/**
* 由spring管理的线程池类,返回的ExecutorService就是给我们来执行线程的
*如果不交给spring管理也是可以的,可以使用单例模式来实现同样功能,但是poolSize *要hardcode了
*
@author
张荣华(ahuaxuan)
*
@version
$Id$
*/
public
class
EasyMailExecutorPool
implements
InitializingBean {
//
线程池大小,spring配置文件中配置
private
int
poolSize;
private
ExecutorService service;
public
ExecutorService getService() {
return
service;
}
public
int
getPoolSize() {
return
poolSize;
}
public
void
setPoolSize(
int
poolSize) {
this
.poolSize
=
poolSize;
}
/**
* 在 bean 被初始化成功之后初始化线程池大小
*/
public
void
afterPropertiesSet()
throws
Exception {
service
=
Executors.newFixedThreadPool(poolSize);
}
}
这样我们就初始化了线程池的大小,接下来就是如何使用这个线程池中的线程了,我们看看MailService是如何来使用线程池中的线程的,这个类中的代码我已经作了详细的解释
代码
/**
* 用来发送 mail 的 service, 其中有一个内部类专门用来供线程使用
*
@author
张荣华(ahuaxuan)
*
@since
2007-7-11
*
@version
$Id$
*/
public
class
EasyMailServieImpl
implements
EasyMailService{
private
static
transient
Log logger
=
LogFactory.getLog(EasyMailServieImpl.
class
);
//
注入MailSender
private
JavaMailSender javaMailSender;
//
注入线程池
private
EasyMailExecutorPool easyMailExecutorPool;
//
设置发件人
private
String from;
public
void
setEasyMailExecutorPool(EasyMailExecutorPool easyMailExecutorPool) {
this
.easyMailExecutorPool
=
easyMailExecutorPool;
}
public
void
setJavaMailSender(JavaMailSender javaMailSender) {
this
.javaMailSender
=
javaMailSender;
}
public
void
setFrom(String from) {
this
.from
=
from;
}
/**
* 简单的邮件发送接口,感兴趣的同学可以在这个基础上继续添加
*
@param
to
*
@param
subject
*
@param
text
*/
public
void
sendMessage(EmailEntity email){
if
(
null
==
email) {
if
(logger.isDebugEnabled()) {
logger.debug(
"
something you need to tell here
"
);
}
return
;
}
SimpleMailMessage simpleMailMessage
=
new
SimpleMailMessage();
simpleMailMessage.setTo(email.getTo());
simpleMailMessage.setSubject(email.getSubject());
simpleMailMessage.setText(email.getText());
simpleMailMessage.setFrom(from);
easyMailExecutorPool.getService().execute(
new
MailRunner(simpleMailMessage));
}
/**
* 发送复杂格式邮件的接口,可以添加附件,图片,等等,但是需要修改这个方法,
* 如何做到添加附件和图片论坛上有例子了,需要的同学搜一下,
* 事实上这里的text参数最好是来自于模板,用模板生成html页面,然后交给javamail去发送,
* 如何使用模板来生成html见 {
@link
http://www.iteye.com/topic/71430
}
*
*
@param
to
*
@param
subject
*
@param
text
*
@throws
MessagingException
*/
public
void
sendMimeMessage(EmailEntity email)
throws
MessagingException {
if
(
null
==
email) {
if
(logger.isDebugEnabled()) {
logger.debug(
"
something you need to tell here
"
);
}
return
;
}
MimeMessage message
=
javaMailSender.createMimeMessage();
MimeMessageHelper helper
=
new
MimeMessageHelper(message);
helper.setTo(email.getTo());
helper.setFrom(from);
helper.setSubject(email.getSubject());
this
.addAttachmentOrImg(helper, email.getAttachment(),
true
);
this
.addAttachmentOrImg(helper, email.getImg(),
false
);
//
这里的text是html格式的, 可以使用模板引擎来生成html模板, velocity或者freemarker都可以做到
helper.setText(email.getText(),
true
);
easyMailExecutorPool.getService().execute(
new
MailRunner(message));
}
/**
* 添加附件或者是图片
*
@param
helper
*
@param
map
*
@param
isAttachment
*
@throws
MessagingException
*/
private
void
addAttachmentOrImg(MimeMessageHelper helper, Map map,
boolean
isAttachment)
throws
MessagingException {
for
(Iterator it
=
map.entrySet().iterator(); it.hasNext();) {
Map.Entry entry
=
(Map.Entry) it.next();
String key
=
(String) entry.getKey();
String value
=
(String) entry.getValue();
if
(StringUtils.isNotBlank(key)
&&
StringUtils.isNotBlank(value)) {
FileSystemResource file
=
new
FileSystemResource(
new
File(value));
if
(
!
file.exists())
continue
;
if
(isAttachment) {
helper.addAttachment(key, file);
}
else
{
helper.addInline(key, file);
}
}
}
}
/**
* 用来发送邮件的 Runnable, 该类是一个内部类,之所以使用内部类,而没有使用嵌套类(静态内部类),
* 是因为内部类可以之间得到 service 的 javaMailSender
* 每次发送邮件都会从线程池中取一个线程, 然后进行发邮件操作
*
@author
ahuaxuan
*/
private
class
MailRunner
implements
Runnable {
SimpleMailMessage simpleMailMessage;
MimeMessage mimeMessage;
/**
* 构造简单文本邮件
*
@param
simpleMailMessage
*/
public
MailRunner(SimpleMailMessage simpleMailMessage) {
if
(mimeMessage
==
null
) {
this
.simpleMailMessage
=
simpleMailMessage;
}
}
/**
* 构造复杂邮件,可以添加附近,图片,等等
*
@param
mimeMessage
*/
public
MailRunner(MimeMessage mimeMessage) {
if
(simpleMailMessage
==
null
) {
this
.mimeMessage
=
mimeMessage;
}
}
/**
* 该方法将在线程池中的线程中执行
*/
public
void
run() {
try
{
if
(simpleMailMessage
!=
null
) {
javaMailSender.send(
this
.simpleMailMessage);
}
else
if
(mimeMessage
!=
null
) {
javaMailSender.send(
this
.mimeMessage);
}
}
catch
(Exception e) {
if
(logger.isDebugEnabled()) {
logger.debug(
"
logger something here
"
, e);
}
}
}
}
}
MailService中的EmailEntity是对邮件的抽象(我只使用了
失血模型,事实上我们也可以让这个EmailEntity来实现Runnable接口,这样Service中的内部类就可以去掉了,同时service中
的大部分代码就要搬到EmailEntity及其父类里了,大家更倾向于怎么做呢?),代码如下:
代码
/**
* 该类是对邮件的抽象,邮件有哪些属性,这个类就有哪些属性 显然这个只是一个例子,
* 这个例子中附带mimemessage发送所需的附件或者图片(如果有的话)
* 需要使用的同学自己扩展
*
*
@author
张荣华(ahuaxuan)
*
@version
$Id$
*/
public
class
EmailEntity {
String to;
String subject;
String text;
//
邮件附件
Map
分享到:
相关推荐
"JDK1.5中的线程池(java.util.concurrent.ThreadPoolExecutor)使用" JDK1.5中的线程池(java.util.concurrent.ThreadPoolExecutor)使用是Java多线程编程中的一种重要概念。随着多线程编程的普及,线程池的使用变得...
JDK1.5中的线程池(java.util.concurrent.ThreadPoolExecutor)使用简介
JDK 1.5引入了java.util.concurrent包,其中包含了线程池的实现,使得并发编程更加便捷和高效。线程池的核心在于它的设计策略,包括核心线程数、最大线程数、线程存活时间、工作队列以及拒绝策略。 线程池的主要类...
在Java中,`java.util.concurrent`包中的`ThreadPoolExecutor`是线程池的主要实现。 **ThreadPoolExecutor的使用** `ThreadPoolExecutor`包含以下几个关键参数: 1. **corePoolSize**:核心线程数,即使没有任务,...
JDK 自带线程池是 Java 语言中用于管理和执行线程的工具,旨在提高多线程编程的效率和灵活性。本文将详细介绍 JDK 自带线程池的组成、创建方法、优点和常见应用场景。 多线程技术 多线程技术是指在一个处理器单元...
backport-util-concurrent库,正如其名,是一种将Java 5及以上版本的并发特性“回移植”到Java 1.4及更早版本的工具,使得开发者能在较旧的Java环境中享受现代并发编程的优势。本文将深入探讨这个库的2.2和3.1两个...
4. **多线程**:`java.lang.Thread`和`java.util.concurrent`包中的类,如`ExecutorService`、`ThreadPoolExecutor`等,展示了如何实现并发和异步执行。 5. **反射机制**:`java.lang.reflect`包中的类,如`Class`...
在JDK 1.5版本之前,Java对线程池的支持非常有限,而在JDK 1.5之后,加入了java.util.concurrent包,其中包含了一系列关于线程池的接口和类,极大地丰富了线程池的应用场景和管理方式。 线程池的主要作用是限制系统...
线程池在Java中是通过`java.util.concurrent`包下的`ThreadPoolExecutor`类实现的。 线程池的主要作用是限制系统中执行线程的数量,通过预先配置好的线程数量,可以避免过多线程导致的资源浪费和系统拥挤,从而提高...
首先,Java中的线程池设计始于JDK 5.0,主要通过`java.util.concurrent`包中的`Executor`接口实现。这个接口仅有一个`execute()`方法,用于提交执行任务。我们也将遵循这个设计,实现一个简单的线程池类`...
Java中,线程池是通过java.util.concurrent.ThreadPoolExecutor类实现的,它为Java应用程序提供了一种有效地管理线程资源的方式。从JDK 1.5开始,Java并发API得到了增强,提供了更为强大的并发工具和库,其中就包括...
线程池的引入始于JDK 1.5,它引入了`java.util.concurrent`包,提供了`Executor`、`ExecutorService`和相关的实现类,如`ThreadPoolExecutor`和`ScheduledThreadPoolExecutor`。 线程池的主要作用是限制系统中同时...
这份JDK_API中文版文档对于学习和掌握Java编程至关重要,它能帮助开发者快速查找和理解类库的使用方法,解决编程过程中遇到的问题。在实际工作中,应经常查阅和参考,以深化对Java的理解和应用。
Java.util.concurrent包是JDK5引入的一个重要更新,它包含了Doug Lea设计的并发库,极大地提升了Java处理并发的能力。这个库提供了高效且易用的多线程工具,包括线程池、同步器和定时任务管理等。 线程池是Java多...
我们可以扩展`java.util.concurrent.ThreadPoolExecutor`类,它提供了更复杂的线程池管理功能,包括工作队列、核心线程数、最大线程数等参数。 3. JDK 中的线程池和工作机制 JDK内置了多种线程池实现,如`...
**JDK1.6 API(应用程序接口)是Java开发工具包的一个核心组成部分,它提供了大量预定义的类和方法,供开发者在构建Java应用程序时使用。API文档详细地阐述了这些类、接口、方法和构造函数的功能、用法以及参数说明...
然而,自JDK 1.5开始,随着`java.util.concurrent`包的引入,Java线程池的功能得到了极大的增强,为开发者提供了更加高效且易于管理的线程管理方式。 #### 二、线程池的作用与必要性 线程池的主要功能在于限制系统...
高级实用工具类是`java.util.concurrent`的核心,包括线程安全集合(如ConcurrentHashMap、CopyOnWriteArrayList等)、线程池(ExecutorService、ThreadPoolExecutor、ScheduledThreadPoolExecutor等)、信号...
8. **并发和多线程**:OpenJDK8的源码中,`java.util.concurrent`包下包含了丰富的并发工具类,如`ExecutorService`, `Semaphore`, `CountDownLatch`等,以及线程池的实现`ThreadPoolExecutor`。 9. **Garbage ...
此外,backport-util-concurrent还实现了`ExecutorService`和`ThreadPoolExecutor`,这是Java 5中的线程池管理框架。`ExecutorService`定义了一组线程执行服务,可以管理和控制并发任务的执行。`ThreadPoolExecutor`...