如何正确关闭BlockingQueue?

时间:2014-07-16 20:10:26

标签: java multithreading

我有一个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
}

2 个答案:

答案 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)

如前所述,使用ExecutorServiceThreadPool,但使用submit Callable而不仅仅是Runnable。让你的工作线程观察一些停止信号(可能AtomicBoolean对所有人都可见)。如果已设置标志,请使Callable返回一个特殊值,表示没有执行任何操作。来电者必须保留Future返回的submitget 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();
    }
}