java线程池的改良思路
传统线程池的思路:第一次任务来临时,当活动线程数小于核心线程数时,new新线程去一一对应任务;当活动线程数大于核心线程时,将任务放在阻塞队列;当阻塞队列满时,不得不又创建线程;当线程数等于最大线程数时,再有任务则不得不执行抛弃策略。由于采用了BlockQueue的take操作,核心线程空闲时将被阻塞从而保持活性,直到有新的任务进入BlockingQueue时被唤醒,那么问题来了,如果第二次任务来临,任务数没有大于核心线程数,则核心任务不会被唤醒。不知道是否是设计者思路存在的bug。
假如这是一个bug那么本文尝试解决这种思路的问题,这样线程池可以多次复用,而且在任务数没有大于核心线程数时,不会遇到核心线程不会被唤醒的问题。
先看一段熟悉的代码
变量声明如下:
private int wc = 0;
// 核心线程数
private volatile int coreThreadPool;
// 最大线程数
private volatile int maxThreadPool;
// 线程存活时间
private volatile long keepAliveTime;
// 工作队列
private volatile BlockingQueue workQueue;
// 构造方法,暂不考虑线程工厂和拒绝执行策略
public SampleThreadPoolExecutor(int coreThreadPool, int maxThreadPool, long keepAliveTime,
TimeUnit unit, BlockingQueue workQueue) {
if (coreThreadPool < 0 || coreThreadPool > maxThreadPool || maxThreadPool <= 0
|| keepAliveTime < 0)
throw new IllegalArgumentException(“arg is illegal …”);
if (workQueue == null)
throw new NullPointerException(“work queue is null …”);
this.coreThreadPool = coreThreadPool;
this.maxThreadPool = maxThreadPool;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.workQueue = workQueue;
}
核心判断,传统思路这里不再赘述,见开头的描述。
@Override
public void execute(Runnable command) {
// 校验参数有效性
if (command == null)
throw new NullPointerException(“command is null …”);
// 如果实际线程数量小于核心线程数,
if (getWorkCount() < coreThreadPool)
// 初始化线程执行任务
addThread(command);
else if (workQueue.offer(command)) {
// 任务放入队列成功,校验核心线程是否为0
if (getWorkCount() == 0)
// 初始化一条不携带任务的线程,让它从队列中获取任务
addThread(null);
} else if (getWorkCount() < maxThreadPool) {
// 初始化非核心线程
addThread(command);
} else if (getWorkCount() >= maxThreadPool)
// 提交任务过多,线程池处理过不来,抛出异常
throw new RejectedExecutionException(
"command id too much, reject execute ...");
}
添加任务的代码
private synchronized void addThread(Runnable task) {
// 由于execute方法调用addThread(null),此处参数非空校验得去掉
// 去掉不保证会有代码恶意调用,所以此方法不能泄露,必须用private修饰
// 为保证代码健壮性,常规参数校验
// if (task == null)
// throw new NullPointerException("taks is null ...");
// 增加一条线程
increaseWork();
new Worker(task).thread.start();
}
// 提供getTask方法从队列中取值
最核心的代码请注意
private Runnable getTask() {
boolean timeOut = false;
for (;😉 {
boolean timed = getWorkCount() > coreThreadPool;
// 利用timed和timeOut一起判断线程是否应该死亡
if (timed && timeOut) {
// 线程即将死亡,wc自减1
decreaseWork();
// 返回为空意味着跳出while循环,线程即将死亡
return null;
}
try {
// 从队列中取值
Runnable task = timed ? workQueue.poll(keepAliveTime,
TimeUnit.NANOSECONDS) : workQueue.take();
if (task != null)
return task;
// 非核心线程获取超时,直接返回null,跳到循环初始处再次和
// timed变量判断,防止核心线程死亡
timeOut = true;
} catch (InterruptedException e) {
timeOut = false;
}
}
}
这是工作线程
private final class Worker implements Runnable {
// 由于Worker只是一个Runnable需要一个thread对象来启动它
private final Thread thread;
private final Runnable task;
public Worker(Runnable task) {
this.task = task;
// 将初始化的线程赋值给thread
this.thread = new Thread(this);
}
@Override
public void run() {
runWorker(this);
}
}
private void runWorker(Worker worker) {
Runnable r = worker.task;
// 此处切记要用while循环而不是用if判断,有以下两个原因:
// 1. 由于addThread有task参数校验,task不可能为null,这就保证了r != null
// 始终条件成立,如果用if,那么r = getTask()将永远不会执行
// 2. 采用while循环也间接保持了线程的活性,后续会解释
while (r != null || (r = getTask()) != null) {
// 执行任务
r.run();
// 注意:使用while循环,r执行完成之后需要显式置空,否则将会造成活锁
r = null;
}
System.out.println(Thread.currentThread().getName() + " will dead …");
}
private synchronized int decreaseWork() {
return --wc;
};
private synchronized int increaseWork() {
return ++wc;
};
private synchronized int getWorkCount() {
return wc;
};
其他代码不再赘述。
问题很明显了,最核心的代码再次引用
private Runnable getTask() {
boolean timeOut = false;
for (;😉 {
boolean timed = getWorkCount() > coreThreadPool;
// 利用timed和timeOut一起判断线程是否应该死亡
if (timed && timeOut) {
// 线程即将死亡,wc自减1
decreaseWork();
// 返回为空意味着跳出while循环,线程即将死亡
return null;
}
try {
// 从队列中取值
Runnable task = timed ? workQueue.poll(keepAliveTime,
TimeUnit.NANOSECONDS) : workQueue.take();
if (task != null)
return task;
// 非核心线程获取超时,直接返回null,跳到循环初始处再次和
// timed变量判断,防止核心线程死亡
timeOut = true;
} catch (InterruptedException e) {
timeOut = false;
}
}
}
这是最核心的代码,重要的代码重复解释一次,如果活动线程数大于核心线程数,任务做完时poll阻塞队列,超时线程移除;直到剩下的活动线程数等于核心线程数,则take阻塞队列一直等待,直到有新的任务进入阻塞队列。这位仁兄注释的很全。
但是这样问题来了,如果我们第二次使用线城池而不是销毁的话,如果任务很少没有放进BlockingQueue则核心线程依然处于等待状态,既不工作又不释放。
为了解决这个问题,我们考虑判断,当BlockingQueue不为空时我们去获取任务,当它为空时,我们用lock去锁定线程,如果活动线程数大于核心线程数则condition.await(time),超时移除;如果活动线程数小于等于核心线程数,我们用condition.wait()直到有新任务来临用condition.signal()激活。代码如下:
核心线程的等待
@SuppressWarnings(“finally”)
public boolean waitUntilNotify()
{
Condition condition = FetchLock(this.getTs().getUuid());
lock.lock();
System.out.println("核心线程:" + Thread.currentThread().getName() + "空闲中...");
try {
condition.await();
System.out.println("等待结束");
} catch (InterruptedException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
System.out.println("中断异常");
return false;
}
finally{
System.out.println("need here");
lock.unlock();
return true;
}
}
非核心线程的等待
public void waitTimeIdle()
{
Condition condition = FetchLock(this.getTs().getUuid());
lock.lock();
try {
condition.awaitNanos(keepAliveTime);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
finally{
lock.unlock();
}
}
非核心线程的移除代码如下
public synchronized Boolean removeWorkThread(workThread wt)
{
if(removeOrNot())
{
decreasWorkingNum();
ThreadList.remove(wt);
System.out.println(getThreadListSize());
System.out.println(“非核心线程:” + wt.getName() + “将销毁…”);
wt.workfree();
return true;
}
return false;
}
结束等待重新激活的代码如下
public void endwait(Runnable task)
{
Condition condition = FetchLock(this.getTs().getUuid());
lock.lock();
System.out.println(“真解锁拉”);
this.getTs().setRn(task);
this.getTs().setIsAvailable(false);
condition.signal();
lock.unlock();
System.out.println(“as1”);
System.out.println(“this.uuid”+this.getId());
}
最核心的代码如下
@Override
public void run()
{
// TODO Auto-generated method stub
while(this.getTs().getIsRunning())
{
while(this.getTs().getRn()!=null||!taskQueque.isEmpty())
{
if(this.getTs().getRn()!=null)
{
System.out.println(“该死终于进来了”);
this.getTs().getRn().run();
this.getTs().setRn(null);
this.getlCTask().TotalNext();
this.setiCounter(this.getlCTask().getTotolNum());
System.out.println(“测试增加的任务数” + this.getlCTask().getTotolNum());
increaseTask();
if(!this.getTs().getIsFirstTask())
{
System.out.println("第一次");
this.getTs().setIsFirstTask(true);
}
}
if(!taskQueque.isEmpty())
{
try{
this.getTs().setRn(taskQueque.take());
} catch (InterruptedException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
if(!this.getTs().getIsAvailable())
{
System.out.println("没到这里么");
this.getTs().setIsAvailable(true);
}
if(this.getTs().getIsAvailable())
{
if(removeOrNot())
{
this.waitTimeIdle();
if(removeWorkThread(this))
{
System.out.println("移除成功拉");
return;
}else
{
System.out.println("核心线程:"+ this.getName() + "存活...");
if(this.waitUntilNotify())
{
System.out.println("这好啊");
System.out.println("uuid" + this.getId());
continue;
}
}
}
else
{
System.out.println("核心线程:"+ this.getName() + "存活...");
if(this.waitUntilNotify())
{
System.out.println("这好啊");
System.out.println("uuid" + this.getId());
continue;
}
}
}
}
}
涉及到的一些方法介绍
判断活动线程数是否大于核心线程数,如果大于则不得不考虑移除
public synchronized boolean removeOrNot()
{
Boolean bflag = getRunThreadCount().get()>coreThreadSize;
return bflag;
}
下面的代码是移除非核心线程的代码
public synchronized Boolean removeWorkThread(workThread wt)
{
if(removeOrNot())
{
decreasWorkingNum();
ThreadList.remove(wt);
System.out.println(getThreadListSize());
System.out.println(“非核心线程:” + wt.getName() + “将销毁…”);
wt.workfree();
return true;
}
return false;
}
调用激活的代码:
public synchronized workThread getAvailableThread(Runnable task)
{
for(workThread wt:this.ThreadList)
{
if(wt.getTs().getIsRunning()&&wt.getTs().getIsAvailable()&&!wt.getTs().getIsFirstTask())
{
System.out.println(“获取成功!”);
wt.endwait(task);
return wt;
}
}
return null;
}
自定义线程如下
public class workThread extends Thread
{
private ThreadState ts;
private LocalCountTask lCTask;
private int iCounter;
public workThread()
{
this.ts = new ThreadState();
}
public void workfree()
{
this.getTs().setIsRunning(false);
this.lCTask = null;
DeleteLock(this.getTs().getUuid());
this.iCounter = 0;
}
public ThreadState getTs() {
return ts;
}
public void setTs(ThreadState ts) {
this.ts = ts;
}
public int getCounterTask() {
// TODO Auto-generated method stub
return this.getiCounter();
}
public int getiCounter() {
return iCounter;
}
public void setiCounter(int iCounter) {
this.iCounter = iCounter;
}
}
线程最关键的状态封装如下
public class ThreadState
{
private Boolean isRunning = null;
private Boolean isAvailable = null;
private Runnable rn = null;
private Boolean isFirstTask = null;
private final String uuid = UUID.randomUUID().toString().replace("-","");
public ThreadState() {
super();
this.rn = null;
this.isRunning = true;
this.isAvailable = false;
this.isFirstTask = false;
}
public Boolean getIsRunning() {
return isRunning;
}
public void setIsRunning(Boolean isRunning) {
this.isRunning = isRunning;
}
public Boolean getIsAvailable() {
return isAvailable;
}
public void setIsAvailable(Boolean isAvailable) {
this.isAvailable = isAvailable;
}
public Runnable getRn() {
return rn;
}
public void setRn(Runnable rn) {
this.rn = rn;
}
public Boolean getIsFirstTask() {
return isFirstTask;
}
public void setIsFirstTask(Boolean isFirstTask) {
this.isFirstTask = isFirstTask;
}
public String getUuid() {
return uuid;
}
}
其他代码都是细枝末节,完整思路已经奉上,如果想看完整代码,需要等到代码上传审核成功之后了,多谢大家的捧场。完整代码链接