我实现了自定义BlockingQueue<T>
,并将其与java.util.concurrent
ArrayBlockingQueue
进行了比较。
这是我的实现方式:
public class CustomBlockingQueue<T> implements BlockingQueue<T> {
private final T[] table;
private final int capacity;
private int head = 0;
private int tail = 0;
private volatile int size;
private final Lock lock = new ReentrantLock();
private final Condition notEmpty = lock.newCondition();
private final Condition notFull = lock.newCondition();
@SuppressWarnings("unchecked")
public CustomBlockingQueue(final int capacity) {
this.capacity = capacity;
this.table = (T[]) new Object[this.capacity];
size = 0;
}
@Override
public void add(final T item) throws InterruptedException {
lock.lock();
try {
while (size >= table.length) {
notFull.await();
}
if (tail == table.length) {
tail = 0;
}
table[tail] = item;
size++;
tail++;
if (size == 1) {
notEmpty.signalAll();
}
} finally {
lock.unlock();
}
}
@Override
public T poll() throws InterruptedException {
lock.lock();
try {
while (size == 0) {
notEmpty.await();
}
if (head == table.length) {
head = 0;
}
final T result = table[head];
table[head] = null;
size--;
head++;
if (size == capacity - 1) {
notFull.signalAll();
}
return result;
} finally {
lock.unlock();
}
}
@Override
public int size() {
return size;
}
}
我的实现基于数组。
我不要求您检查代码,但可以帮助我弄清我的代码与Java的区别。
在我的代码中,我在notEmpty.signalAll()
子句中执行notFull.signalAll()
或if
,但是java.util.concurrent
在每种情况下仅调用signal()
?
即使没有必要,每次通知另一个线程的原因是什么?
答案 0 :(得分:0)
如果某个线程在可以读取队列或添加到队列之前一直处于阻塞状态,则该线程的最佳位置是在适用条件的等待集中。这样一来,它就不会主动争夺锁,也不会被上下文切换到其中。
如果只有一项添加到队列中,我们只希望发信号通知一位消费者。我们不想向更多的消费者发出信号,而不是让队列中有更多的项目,因为这使系统不得不管理更多工作,并给所有无法取得进展的线程分配时间片。
这就是为什么ArrayBlockingQueue每次将一个项目入队或出队时每次发出一个信号的原因,以避免不必要的唤醒。在您的实现中,等待集中的每个人(从空到非空,或从满到不满)都会被唤醒,无论这些线程中有多少个能够完成其工作。
随着越来越多的线程同时达到这个目标,这一点变得更加重要。想象一下一个有100个线程正在等待消耗队列中某些内容的系统,但是每10秒仅添加一项。最好不要从waitset中踢出100个线程,只是要让其中的99个线程返回。