带有isEmpty()通知的队列更改

时间:2011-01-31 11:16:05

标签: java concurrency queue scheduling

我在生产者 - 消费者环境中有一个BlockingQueue<Runnable>(取自ScheduledThreadPoolExecutor)。有一个线程向队列添加任务,以及一个执行它们的线程池。

我需要关于两个事件的通知:

  1. 第一项添加到空队列
  2. 从队列中删除的最后一项
  3. 通知=将消息写入数据库。

    有没有明智的方法来实现它?

2 个答案:

答案 0 :(得分:1)

一种简单而天真的方法是用一个只需检查底层队列的实现来装饰你的BlockingQueue,然后发布一个任务来进行通知。

NotifyingQueue<T> extends ForwardingBlockingQueue<T> implements BlockingQueue<T> {
  private final Notifier notifier; // injected not null

  …

  @Override public void put(T element) {
    if (getDelegate().isEmpty()) {
      notifier.notEmptyAnymore();
    }
    super.put(element);
  }

  @Override public T poll() {
    final T result = super.poll();
    if ((result != null) && getDelegate().isEmpty())
      notifier.nowEmpty();
  }
  … etc
}

这种方法虽然存在一些问题。虽然空 - &gt; notEmpty非常简单 - 特别是对于单个生产者案例,两个消费者很容易同时运行,并且看到队列都是非空的 - &gt;空。

但是,如果您想要通知所有队列在某个时间,那么这就足够了,只要您的通知程序是您的状态机,跟踪空虚和非空虚就足够了并通知它何时从一个变为另一个:

AtomicStateNotifier implements Notifier {
  private final AtomicBoolean empty = new AtomicBoolean(true); // assume it starts empty
  private final Notifier delegate; // injected not null

  public void notEmptyAnymore() {
    if (empty.get() && empty.compareAndSet(true, false))
      delegate.notEmptyAnymore();
  }

  public void nowEmpty() {
    if (!empty.get() && empty.compareAndSet(false, true))
      delegate.nowEmpty();
  }
}

现在这是一个线程安全防范实际的Notifier实现,它可能会将任务发送给Executor,以异步地将事件写入数据库。

答案 1 :(得分:0)

设计很可能存在缺陷,但你可以做到相对简单: 您添加了一个线程,因此您可以在添加之前进行检查。即pool.getQueue().isEmpty() - w / one producer,这是安全的。

无法保证删除最后一项,但您可以覆盖beforeExecute并再次检查队列。可能在isEmpty()返回true后发生小超时。可能以下代码最好在afterExecute中执行。

protected void beforeExecute(Thread t, Runnable r) { 
  if (getQueue().isEmpty()){
    try{
      Runnable r = getQueue().poll(200, TimeUnit.MILLISECONDS);
      if (r!=null){ 
        execute(r);
      }  else{
        //last message - or on after execute by Setting a threadLocal and check it there
        //alternatively you may need to do so ONLY in after execute, depending on your needs
     }
    }catch(InterruptedException _ie){
      Thread.currentThread().interrupt();
    }
  }
}

有时像那样

我可以解释为什么做队列本身的通知不能正常工作:想象你添加一个要由池执行的任务,任务立即安排,队列再次为空,你需要通知。< / p>