手写线程池的改良思路

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;
}

}
其他代码都是细枝末节,完整思路已经奉上,如果想看完整代码,需要等到代码上传审核成功之后了,多谢大家的捧场。完整代码链接

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值