您的位置:首页 > 编程语言 > Java开发

TimeoutFutureTask实现

2015-09-09 23:04 369 查看
Java1.5的
java.util.concurrent
包封装了一系列异步处理操作的工具,简化了多线程程序的开发。其中一个重要的工具类就是Executors。

通过他提供的工厂方法我们可以很方便的就生成线程池的实例,例如获取一个固定线程池大小的实例只需要调用
Executors.newFixedThreadPool(MAX_THREAD_COUNT)
,其中
MAX_THREAD_COUNT
就是我们需要的线程池大小。这个方法其实是生成了一个ThreadPool的实例

public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}


再看看ThreadPoolExecutor的完整构造方法

public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}


corePoolSize是指可以运行的核心线程数量,maximumPoolSize是指可以运行的最大线程数量,keepAliveTime是指如果线程数量超过了核心线程数量的那部分线程(既maximumPoolSize-corePoolSize那部分)如果空闲的最大存活时间,意即在超过这个时间之后只保留核心线程数量的线程继续在后台中等待执行,让系统的开销变得更加合理,最后一个参数RejectedExecutionHandler是指如果线程无法进入等待队列的拒绝处理(拒绝策略),其他几个参数就不再一一介绍了。

通过Executors.newFixedThreadPool生成的线程池一旦核心线程都处于运行中的时候,接下来的线程要么继续被加入等待队列中(因为未制定等待线程的容量,所以线程都会被加入等待队列中),要么被拒绝。如果核心线程池中的线程永远不退出,那么新加入的线程则永远无法执行,在我们目前的项目中就碰到了这样的情况。怎么办?修改策略,与其让他们一直占用着资源,不如让这些流氓退出,让新的线程进可以继续执行。

于是我们实现了一个FutureTask的子类TimeoutFutureTask来作为扩充:

private static class TimeoutFutureTask<V> extends FutureTask<V> {

private long startTime = System.currentTimeMillis();

public TimeoutFutureTask(Callable<V> callable) {
super(callable);
}

public TimeoutFutureTask(Runnable runnable, V result) {
super(runnable, result);
}

public boolean isExpire() {
return System.currentTimeMillis() - startTime > MAX_EXECUTION_TIME;
}
}


这里主要引入了一个额外的属性
private long startTime = System.currentTimeMillis();
通过记录线程创建时间,通过它计算计算线程是否过期
isExpire()


再引入一个线程放在static代码块内(让这个线程随着第一个线程被启动时启动在后台):

private static final long MAX_EXECUTION_TIME = 10 * 60 * 1000;

private static final Vector<TimeoutFutureTask> FUTURE_TASK_VECTOR = new Vector<>();

private volatile static boolean IS_CHECK_THREAD_STARTED = false;

static {
if (!IS_CHECK_THREAD_STARTED) {
new Thread(new Runnable() {
@Override
public void run() {
IS_CHECK_THREAD_STARTED = true;
while (FUTURE_TASK_VECTOR != null) {
try {
System.out.println("Check thread.");
try {
Thread.sleep(MAX_EXECUTION_TIME);
} catch (InterruptedException e) {
e.printStackTrace();
}
Iterator<TimeoutFutureTask> iterator = FUTURE_TASK_VECTOR.iterator();
while (iterator.hasNext()) {
TimeoutFutureTask task = iterator.next();
System.out.printf("Task start at: %s%n", new Date(task.startTime));
try {
if (task.isExpire()) {
System.out.println("Task expired.");
iterator.remove();
if (task.isDone() || task.isCancelled()) {
continue;
}
task.cancel(true);
}
} catch (Exception e) {
e.printStackTrace();
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
IS_CHECK_THREAD_STARTED = false;
}
}).start();
}
}


一旦线程超时未被执行就取消(这里的检查时间间隔MAX_EXECUTION_TIME,考虑到最坏情况,线程最多的存活时间:MAX_EXECUTION_TIME*2)

if (task.isExpire()) {
System.out.println("Task expired.");
iterator.remove();
if (task.isDone() || task.isCancelled()) {
continue;
}
task.cancel(true);
}


这样就解决了超时线程在合理的时间超时退出的问题。

下一步还应该继续优化处理报告,需要有一个超时报告,将超时的线程信息写日志做分析。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  多线程 java 线程池