我有一个BlockingQueue,用于处理单个后台线程上的工作事件。各种线程调用add
向队列添加一些工作,单个后台线程调用take
来获取工作并一次处理它。最终可能是时候停止处理工作了,我想确保请求工作的来电者得到他们的结果或者null
表明他们的工作没有完成,因为BlockingQueue
正在关闭
如何干净地停止接受新作品,我能想到的最好的方法是将BlockingQueue
字段设置为null
,然后在调用NullPointerException
时捕获add
。在将字段设置为null
之前,我将保留指针的本地副本,以便在它停止接受工作后我可以将其耗尽。我认为这会有效,但看起来有点笨拙,有没有正确的方法来做到这一点?
以下是代码的样子:
ArrayBlockingQueue<Command> commandQueue =
new ArrayBlockingQueue<Command>(100, true);
public boolean addToQueue(Command command) {
try {
return commandQueue.add(command);
} catch (IllegalStateException e) {
return false;
}
}
@Override
public void run() {
try {
while (!Thread.currentThread().isInterrupted()) {
Command command = commandQueue.take();
// ... work happens here
// result is sent back to caller
command.provideResponseData(response);
}
} catch (InterruptedException e) {
// Break out of the loop and stop
}
// TODO: stop accepting any new work, drain the queue of existing work
// and provide null responses
}
答案 0 :(得分:0)
不要使用BlockingQueue和工作线程,而应考虑使用单线程ThreadPoolExecutor。像这样:
private class CommandRunner implements Runnable {
public CommandRunner(Command command) {
this.command = command;
}
public void run() {
// ... work happens here
// result is sent back to caller
command.provideResponseData(response);
}
}
private ExecutorService commandExecutor = Executors.newSingleThreadExecutor();
public boolean addToQueue(Command command) {
commandExecutor.submit(new CommandRunner(command));
}
然后你的关机方法可以委托给执行者。
答案 1 :(得分:0)
如前所述,使用ExecutorService
或ThreadPool
,但使用submit
Callable
而不仅仅是Runnable
。让你的工作线程观察一些停止信号(可能AtomicBoolean
对所有人都可见)。如果已设置标志,请使Callable
返回一个特殊值,表示没有执行任何操作。来电者必须保留Future
返回的submit
至get
Callable
的结果。
也许我应该详细说明一下。如果您当前正在使用Runnable
,可以将它们包含在Callable
中,并在call
中检查停止标记。如果在调用ExecutorService.shutdown之前设置停止标志,它将正常完成当前作业,但会有效取消所有剩余作业,从而快速耗尽剩余队列。如果不关闭,甚至可以在重置停止标志后重用ExecutorService。
static enum EResult {
Cancelled, Completed
}
static abstract class MyCallable implements Callable<EResult> {
Runnable runner;
public MyCallable( Runnable runner) {
super();
this.runner = runner;
}
}
static AtomicBoolean cancelled = new AtomicBoolean( false);
static void main( String[] argv) {
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println( "Done");
}
};
Callable<EResult> callable = new MyCallable( runnable) {
@Override
public EResult call() throws Exception {
if ( cancelled.get()) {
return EResult.Cancelled;
}
runner.run();
return EResult.Completed;
}
};
ExecutorService executorService = Executors.newFixedThreadPool( 1);
// while submitting jobs, change cancelled at some point
Future<EResult> future = executorService.submit( callable);
try {
EResult completeOrNot = future.get();
System.out.println( "result: " + completeOrNot);
} catch ( InterruptedException e) {
e.printStackTrace();
} catch ( ExecutionException e) {
e.printStackTrace();
}
}