我只想使用ReentrantLock实现阻塞队列,我定义了两个条件full
和empty
,其源代码如下:
@Slf4j
@NotThreadSafe
public class CustomBlockQueue<T> {
private ReentrantLock lock = new ReentrantLock();
private Condition full = lock.newCondition();
private Condition empty = lock.newCondition();
private Integer maxLength = 1 << 4;
private Integer putIndex = 0, takeIndex = 0;
private Integer count = 0;
private Object[] value;
public BlockQueue(){
value = new Object[maxLength];
}
public BlockQueue(Integer maxLength){
this.maxLength = maxLength;
value = new Object[maxLength];
}
public void put(T val) throws InterruptedException {
lock.lock();
try {
if (count.equals(maxLength)){
log.info("The queue is full!");
full.await();
}
putIndex = putIndex % maxLength;
value[putIndex++] = val;
count++;
empty.signal();
}finally {
lock.unlock();
}
}
@SuppressWarnings("unchecked")
public T take() throws InterruptedException {
lock.lock();
Object val;
try {
if (count == 0){
empty.await();
}
takeIndex = takeIndex % maxLength;
val = value[takeIndex++];
count--;
full.signal();
}finally {
lock.unlock();
}
return (T) val;
}
}
在两个使用者线程和一个提供者线程中进行测试时,count
在某些偶然的时间内小于零。
为什么阻塞队列不是线程安全的,谁可以帮助我,给我一些指导?非常感谢您!
如果我只使用一个Condition
,它可以正确运行吗?源代码如下:
@Slf4j
@NotThreadSafe
public class CustomBlockQueue<T> {
private ReentrantLock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
...
public void put(T val) throws InterruptedException {
lock.lock();
try {
while (count.equals(maxLength)){
log.info("The queue is full!");
condition.await();
}
putIndex = putIndex % maxLength;
value[putIndex++] = val;
count++;
condition.signal();
}finally {
lock.unlock();
}
}
@SuppressWarnings("unchecked")
public T take() throws InterruptedException {
lock.lock();
Object val;
try {
while (count == 0){
condition.await();
}
takeIndex = takeIndex % maxLength;
val = value[takeIndex++];
count--;
condition.signal();
}finally {
lock.unlock();
}
return (T) val;
}
}
答案 0 :(得分:3)
一个类似的问题:Why should wait() always be called inside a loop
考虑这种情况:
lock.lock();
上被阻止empty.await();
上被阻止。count = 1
并调用empty.signal();
。empty.await();
中唤醒,它需要重新获取锁,而消费者1 在它之前。 / li>
消费者2 获得了锁,因为它已执行
if (count == 0){ <--- consumer 2 will not re-check this condition
empty.await();
}
消费者2 认为队列不为空,然后执行:
takeIndex = takeIndex % maxLength;
val = value[takeIndex++];
count--;
这使计数减为0。
使用while
而不是if
保证,消费者2将重新检查队列是否为空,从而保证count >= 0
。
while (count == 0){
empty.await();
}
另外,最好用Produce方法做同样的事情:
while (count.equals(maxLength)){
log.info("The queue is full!");
full.await();
}
答案 1 :(得分:1)
一个明显的事情是,Condition可以“唤醒”,而无需相应调用“ signal”。因此,您需要使用“ while”而不是“ if”。例如:
if(pass.matches("^[A-Z|a-z|0-9|#|$|&|_]{8}")) flag =true;
另请参见javadoc:https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/locks/Condition.html
与此条件相关联的锁被自动释放,并且 当前线程出于线程调度目的而被禁用,并且 处于休眠状态,直到发生以下四种情况之一:
- 其他一些线程为此条件调用signal()方法,并且当前线程恰好被选择为要使用的线程。 唤醒或
- 其他一些线程为此条件调用signalAll()方法;或
- 其他一些线程中断了当前线程,并且支持中断线程挂起;或
- 发生“虚假唤醒”。