Thread.yield()有更好的解决方案吗?

时间:2011-10-26 09:54:14

标签: java multithreading

我创建了一个扩展ArrayBlockingQueue的CloseableBlockingQueue:

private static class CloseableBlockingQueue<E> extends ArrayBlockingQueue<E> {
// Flag indicates closed state.
private volatile boolean closed = false;

public CloseableBlockingQueue(int queueLength) {
  super(queueLength);
}

/***
 * Shortcut to do nothing if closed.
 */
@Override
public boolean offer(E e) {
  return closed ? true : super.offer(e);
}

/***
 * Shortcut to do nothing if closed.
 */
@Override
public void put(E e) throws InterruptedException {
  if (!closed) {
    super.put(e);
  }
}

/***
 * Shortcut to do nothing if closed.
 */
@Override
public E poll() {
  return closed ? null : super.poll();
}

/***
 * Shortcut to do nothing if closed.
 * @throws InterruptedException 
 */
@Override
public E poll(long l, TimeUnit tu) throws InterruptedException {
  return closed ? null : super.poll(l, tu);
}

/***
 * Mark me as closed and clear the queue.
 */
void close() {
  closed = true;
  // There could be more threads blocking on my queue than there are slots 
  // in the queue. We therefore MUST loop.
  do {
    // so keep clearing
    clear();
    /* Let others in ... more specifically, any collectors blocked on the 
     * queue should now unblock and finish their put.
     * 
     * Subsequent puts should shortcut but it is the waiting ones I need
     * to clear.
     */
    Thread.yield();
    /* Debug code.
    // Did yield achieve?
    if (!super.isEmpty()) {
     * This DOES report success.
      log("! Thread.yield() successful");
    }
     * 
     */
    // Until all are done.
  } while (!super.isEmpty());
}

/***
 * isClosed
 * 
 * @return true if closed.
 */
boolean isClosed() {
  return closed;
}
}

我关注的是close方法,它试图重新启动队列中阻塞的任何线程。我使用Thread.yield()来尝试,但是我看到了一些引用,表明这种技术可能并不总是有效,因为不能保证在收益期间会唤醒任何其他被阻塞的线程。

队列用于将多个线程的输出集中到单个流中。与队列中的插槽相比,可以轻松地提供更多线程,因此队列很可能已满并且当它关闭时,有几个线程阻塞它。

我欢迎你的想法。

感谢Tom的建议,我已经重构:

  • 保留可能阻塞的所有线程的集合。
  • 关闭时,中断所有这些。
BTW:由于线程集合主要用于添加对象并几乎立即删除相同的对象,我从http://www.java2s.com/Code/Java/Collections-Data-Structure/ConcurrentDoublyLinkedList.htm获取了Doug Lea令人印象深刻的ConcurrentDoublyLinkedList的副本,并添加了几种方法以允许我保持持有添加的节点。然后去除应该是O(1)而不是O(n)。

3 个答案:

答案 0 :(得分:1)

我认为yield()根本不会影响队列中阻塞的线程。

如果你可以跟踪等待的线程(如果你正在包装阻塞方法,应该是直截了当的)。你关闭时可以对它们调用interrupt()。

请参阅此问题:ArrayBlockingQueue - How to "interrupt" a thread that is wating on .take() method

答案 1 :(得分:0)

使用wait/notifyAll()或最好使用util.concurrent包中的一个同步原语(例如CountDownLatch),而不是yield-while-check循环。将对象放在队列的末尾,在处理时触发通知。使调用线程(close方法)等待此通知。它将一直睡眠,直到队列耗尽。

答案 2 :(得分:0)

我会把毒丸放入队列。例如空值。 当等待线程获得药丸时,它会将其放回队列中。

E pill = null;

void close() {
   closed = true;
   clear();
   while(!offer(pill)); // will wake one thread.
}

public E poll() {
   if (closed) return null;
   E e = super.poll();
   if (e == pill) add(e); // will wake one thread.
   return e;
}