线程池就是一个线程的容器,每次只执行额定数量的线程。java.util.concurrent.ThreadPoolExecutor 就是这样的线程池。
1. 构造函数的介绍
线程池类为 java.util.concurrent.ThreadPoolExecutor ,常用构造方法为:
public ThreadPoolExecutor( int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
RejectedExecutionHandler handler) {
this (corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors. defaultThreadFactory (), handler);
}
corePoolSize指的是保留的线程池大小。
maximumPoolSize指的是线程池的最大大小。
keepAliveTime指的是空闲线程结束的超时时间。
unit是一个枚举,表示keepAliveTime 的单位。
workQueue表示存放任务的队列。
1. 线程池工作过程
线程池的工作过程如下:
1、线程池刚创建时,里面没有一个线程。任务队列是作为参数传进来的。不过,就算队列里面有任务,线程池也不会马上执行它们。
2、当调用 execute() 方法添加一个任务时,线程池会做如下判断:
a. 如果正在运行的线程数量小于 corePoolSize,那么马上创建线程运行这个任务;
b. 如果正在运行的线程数量大于或等于 corePoolSize,那么将这个任务放入队列。
c. 如果这时候队列满了,而且正在运行的线程数量小于maximumPoolSize,那么还是要创建线程运行这个任务;
d. 如果队列满了,而且正在运行的线程数量大于或等于maximumPoolSize,那么线程池会抛出异常,告诉调用者“我不能再接受任务了”。
3、当一个线程完成任务时,它会从队列中取下一个任务来执行。
4、当一个线程无事可做,超过一定的时间(keepAliveTime)时,线程池会判断,如果当前运行的线程数大于corePoolSize,那么这个线程就被停掉。所以线程池的所有任务完成后,它最终会收缩到corePoolSize 的大小。
2. 工程中的编码
ThreadControl.java
/**
* 线程控制类,UserPrivilege.threadNUM必须自此之前被赋值<br>
* 单件模式
*/
public class ThreadControl {
/**
* 线程池
*/
public static ThreadPoolExecutor threadPool;
private ThreadControl() {
// 构造一个线程池
// 参数说明如下:
// corePoolSize: 线程池维护线程的最少数量
// maximumPoolSize:线程池维护线程的最大数量
// keepAliveTime: 线程池维护线程所允许的空闲时间
// unit: 线程池维护线程所允许的空闲时间的单位
// workQueue: 线程池所使用的缓冲队列
// handler: 线程池对拒绝任务的处理策略
threadPool = new ThreadPoolExecutor(UserPrivilege.threadNUM,
UserPrivilege.threadNUM, 3, TimeUnit.SECONDS,
new ArrayBlockingQueue<Runnable>(UserPrivilege.threadNUM / 2),
new ThreadPoolExecutor.CallerRunsPolicy());
}
private static final ThreadControl instance = new ThreadControl();
public static ThreadControl getInstance() {
return instance;
}
}
Privilege.java如下
/**
* 用户权限 配合线程线程控制,UserPrivilege.threadNUM必须先赋值,再调用ThreadControl
*/
public class UserPrivilege {
/**
* 用户可以同时发起的线程数目
*/
public static int threadNUM;
}
调用线程池的代码位于服务器端的MQAccessServer.java中
/**
* 服务器端访问MQ类,产生对外接口
* <p>
* MQSender类实现了Runnable接口,这是为了客户端在向MQ发送信息时可以以线程方式进行调用<br>
**/
public class MQAccessServer {
/**
* 关闭线程池
* <p>
* 在用户点击Receive按钮后调用<br>
*
* @return 关闭线程池成功返回true
*/
public boolean shutDownThreadPool() {
ThreadControl.threadPool.shutdown();
return true;
}
/**
* 用户调用此函数进行向MQ队列中发送信息,
* <p>
* 在此函数通过session查询用户是否登陆,登陆后才可以进行发送<br>
*
* @param str0
* 选项,后期使用,这里用于选择数据发送的目标队列
* @param str
* 发送内容
* @return string 输出操作log日志
*/
public String send(String str0, String str) {
// 通过session查询用户是否登陆
MessageContext mc = MessageContext.getCurrentMessageContext();
ServiceGroupContext sgc = mc.getServiceGroupContext();
if (sgc == null || sgc.getProperty("userName") == null) {
return "未登录,不能执行业务操作";
}
MQSender mqSender = null;
if (str0.equals("1"))// 选择数据发送的目标队列
{
mqSender = new MQSender("XIR_QM_1502", "10.23.117.134", 1502, 1381,
"SYSTEM.DEF.SVRCONN", "ESBREQ");
// mqSender.sendMessage(str);
mqSender.setParaSender(1, str, "0001", 8, 0);
// 通过线程池方式调用发送线程
ThreadControl.threadPool.execute(mqSender);
}
return (MQSender.threadName + " ===== is running, 操作员:" + sgc
.getProperty("userName"));
}
程序中采取了ThreadControl单件模式,这样可以保证用户登录后发起的线程调用都是被同一个线程池所管理。