本案例是学习自《Java并发编程之美》第十一章第九小节整理总结而来!推荐大家学习此书!
代码案例
我们设定一个单线程线程池并且队列设置为只能容纳一个任务,否则就执行DiscardPolicy的拒绝策略!然后开始执行下面代码,看看问题所在!
public class FutureDemo {
public static void main(String[] args) throws ExecutionException, InterruptedException, TimeoutException {
ThreadPoolExecutor executor = new ThreadPoolExecutor(1,
1,
1l,
TimeUnit.SECONDS,
new ArrayBlockingQueue<Runnable>(1),
new ThreadPoolExecutor.DiscardPolicy());
Future future1 = executor.submit(new Runnable() {
@SneakyThrows
@Override
public void run() {
System.out.println("start task 1");
Thread.sleep(1000);
}
});
Future future2 = executor.submit(new Runnable() {
@SneakyThrows
@Override
public void run() {
System.out.println("start task 2");
}
});
Future future3 = null;
try {
future3 = executor.submit(new Runnable() {
@SneakyThrows
@Override
public void run() {
System.out.println("start task 3");
}
});
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("return task 1 :" + future1.get());
System.out.println("return task 2 :" + future2.get());
System.out.println("return task 3 :" + future3 == null ? null : future3.get());
executor.shutdown();
}
}
运行结果,执行完前两个任务后,第三个任务一直处于阻塞状态:
如果去掉future3.get()后,就不会发生阻塞问题了:
问题分析
当使用DiscardPolicy或者DiscardOldestPolicy策略的时候,调用future的get方法时会出现一直阻塞的问题!看源码我们得知,是由于任务三的状态=0,小于COMPLETING(1),所以一直阻塞,无法继续执行report方法:
public V get() throws InterruptedException, ExecutionException {
int s = state;
// 小于1 就会一直阻塞
if (s <= COMPLETING)
s = awaitDone(false, 0L);
return report(s);
}
/**
* FutureTask是有状态的,初始状态就是NEW
*
* Possible state transitions:
* NEW -> COMPLETING -> NORMAL
* NEW -> COMPLETING -> EXCEPTIONAL
* NEW -> CANCELLED
* NEW -> INTERRUPTING -> INTERRUPTED
*/
private volatile int state;
private static final int NEW = 0;
private static final int COMPLETING = 1;
private static final int NORMAL = 2;
private static final int EXCEPTIONAL = 3;
private static final int CANCELLED = 4;
private static final int INTERRUPTING = 5;
private static final int INTERRUPTED = 6;
由submit方法进入也是可以窥见的,Runnable转化为FutureTask,state = NEW
public Future<?> submit(Runnable task) {
if (task == null) throw new NullPointerException();
// submit-1
RunnableFuture<Void> ftask = newTaskFor(task, null);
// submit-2
execute(ftask);
return ftask;
}
// submit-1
protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) {
return new FutureTask<T>(runnable, value);
}
public FutureTask(Runnable runnable, V result) {
this.callable = Executors.callable(runnable, result);
this.state = NEW; // ensure visibility of callable
}
继续看一下线程池如何执行一个任务的:
// submit-2
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
int c = ctl.get();
// 线程个数小于核心线程数 创建线程执行
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
}
// 目前线程数=核心线程数,任务放入队列
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
if (! isRunning(recheck) && remove(command))
reject(command);
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
// 尝试新增线程执行任务,失败就拒绝任务
else if (!addWorker(command, false))
reject(command);
}
看一下拒绝策略DiscardPolicy ,就是什么都不做,所以放弃的任务状态仍处于NEW,因此,当拒绝策略没有处理FutureTask的状态时,或者没有throw 错误的时候就会一直阻塞状态:
public static class DiscardPolicy implements RejectedExecutionHandler {
/**
* Creates a {@code DiscardPolicy}.
*/
public DiscardPolicy() { }
/**
* Does nothing, which has the effect of discarding task r.
*
* @param r the runnable task requested to be executed
* @param e the executor attempting to execute this task
*/
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
}
}
最佳实践
自定义拒绝策略:
public class FutureDemo1 {
public static void main(String[] args) throws ExecutionException, InterruptedException, TimeoutException {
ThreadPoolExecutor executor = new ThreadPoolExecutor(1,
1,
1l,
TimeUnit.SECONDS,
new ArrayBlockingQueue<Runnable>(1),
new MyPolicy());
Future future1 = executor.submit(new Runnable() {
@SneakyThrows
@Override
public void run() {
System.out.println("start task 1");
Thread.sleep(1000);
}
});
Future future2 = executor.submit(new Runnable() {
@SneakyThrows
@Override
public void run() {
System.out.println("start task 2");
}
});
Future future3 = null;
try {
future3 = executor.submit(new Runnable() {
@SneakyThrows
@Override
public void run() {
System.out.println("start task 3");
}
});
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("return task 1 :" + future1.get());
System.out.println("return task 2 :" + future2.get());
try {
System.out.println("return task 3 :" + future3 == null ? null : future3.get());
} catch (Exception e) {
e.printStackTrace();
}
executor.shutdown();
}
static class MyPolicy implements RejectedExecutionHandler {
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
((FutureTask)r).cancel(true);
}
}
}
或者使用超时方法:
try {
System.out.println("return task 3 :" + future3 == null ? null : future3.get(5, TimeUnit.SECONDS));
} catch (Exception e) {
e.printStackTrace();
}
或者使用AbortPolicy策略,如下: