有这样的一个需求:
1、需要一个线程池(Java 1.4);
2、加入的线程可以指定不同的执行时间;
3、当执行时间到且线程池没满,则立即执行该线程;
4、如果执行时间到但线程池已满,则像普通的线程池一样阻塞直到线程池中有可用的位置。
下面是写出来的代码,包含简单的测试。
import java.util.Date;
import java.util.Vector;
import java.util.Calendar;
/**
* 带定时执行功能的线程池。
* 注意:
* <ol>
* <li>没有超时的设置,不会强制结束其中的线程。</li>
* <li>即使线程池没满,也不能严格保证线程按照既定时间
* 执行,通常会延迟 0.01 秒左右。不过对大多数情况来说这
* 是可以接受的。</li>
* </ol>
*/
public class TimedThreadPool {
private int size;
// 线程列表。注意,只有正在执行的线程才会出现在这个列表中。
private Vector threads = new Vector();
private final Lock lock = new Lock();
/**
* 构造函数
*
* @param size 线程池大小。在执行过程中可以随时调整。
*/
public TimedThreadPool(int size) {
this.size = size;
}
/**
* 添加一个定时执行的线程
*
* @param thread 要执行的线程
* @param executeTime 要执行的时间
*/
public void add(Thread thread, Date executeTime) {
new PooledThread(executeTime, thread, this).start();
}
/**
* 添加一个 ExecutableThread 对象
*
* @param thread ExecutableThread 对象
*/
protected void addExecutable(PooledThread thread) {
threads.add(thread);
}
/**
* 删除一个 ExecutableThread 对象
*
* @param thread ExecutableThread 对象
*/
protected void remove(PooledThread thread) {
threads.remove(thread);
}
/**
* 判断线程池是否满了
*
* @return 如果正在运行的线程数等于或超过指定大小
*/
public boolean isFull() {
return threads.size() >= size;
}
/**
* 获取线程池大小
*
* @return 线程池大小
*/
public int getSize() {
return size;
}
/**
* 设置线程池大小。注意,缩小线程池不会马上结束正在执行的线程。
*
* @param size 新的线程池大小
*/
public void setSize(int size) {
this.size = size;
}
/**
* 获得当前正在运行的线程数量
*
* @return 当前正在运行的线程数量
*/
public int getRunningThreadNum() {
return threads.size();
}
/**
* 获得锁对象
*
* @return 锁对象
*/
public Lock getLock() {
return lock;
}
/**
* 测试
*
* @param args 命令行参数
*
* @throws Exception 如果出现错误
*/
public static void main(String[] args) throws Exception {
TimedThreadPool p = new TimedThreadPool(3);
for (int i = 0; i < 10; i++) {
TestThread tt = new TestThread(i + 1, p);
System.out.println("++ add thread " + (i + 1));
p.add(tt, later());
Thread.sleep(270);
}
}
// 测试方法:获得当前两秒后的时间
private static Date later() {
Calendar c = Calendar.getInstance();
c.add(Calendar.SECOND, 2);
return c.getTime();
}
}
/**
* 锁类
*/
class Lock {
}
/**
* 包装线程
*/
class PooledThread extends Thread {
private Date executeDate;
private Thread executable;
private TimedThreadPool pool;
/**
* 构造函数
*
* @param executeDate 执行时间
* @param executable 要执行的线程
* @param pool 线程池对象
*/
PooledThread(Date executeDate, Thread executable, TimedThreadPool pool) {
this.executeDate = executeDate;
this.executable = executable;
this.pool = pool;
}
public void run() {
Lock lock = pool.getLock();
boolean finished = false;
try {
while (!finished) {
boolean runnable = executeDate.before(new Date());
// 判断能否执行。如果能够执行则占个位子,然后释放锁对象
synchronized (lock) {
if (!pool.isFull() && runnable) {
pool.addExecutable(this);
} else if (pool.isFull()) {
// 如果执行时间到了但线程池是满的,则阻塞
lock.wait();
}
}
// 开始执行线程。线程的执行是异步的,所以这里不应该把持锁对象。
if (runnable) {
executable.run();
pool.remove(this);
finished = true;
// 启动阻塞的其他线程
synchronized (lock) {
lock.notify();
}
}
// 如果执行时间没到,那么到这里 finished 为 false。
// 问题是如果执行时间没到且线程池没满,那么这里将会是一个无限循环,CPU 占用 100%。
// 所以这里加上一点休息时间。
Thread.sleep(10);
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
pool.remove(this);
}
}
}
/**
* 测试线程
*/
class TestThread extends Thread {
protected int id;
private TimedThreadPool pool;
TestThread(int id, TimedThreadPool pool) {
this.id = id;
this.pool = pool;
}
public void run() {
try {
System.out.println(">>> thread " + id + " started, running threads: "
+ pool.getRunningThreadNum());
Thread.sleep(2000);
System.out.println("=== thread " + id + " finishing, running threads: "
+ pool.getRunningThreadNum());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}