我创建了一个扩展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的建议,我已经重构:
保
答案 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;
}