这可能是一个愚蠢的问题,但我无法理解为什么这个类中的两个不同线程可以使用单个ReentrantLock(这是我用来玩线程和锁的ArrayBlockingQueue的简化解决方案):
class SampleThreadSafeQueue {
private ReentrantLock lock = new ReentrantLock();
private List<Integer> list = new ArrayList<>();
private Condition notEmpty;
private Condition notFull;
private volatile int count;
public SampleThreadSafeQueue() {
notEmpty = lock.newCondition();
notFull = lock.newCondition();
}
public int take() {
try {
lock.lock();
System.out.println(Thread.currentThread().getName() +": acquired lock in take()");
while (count == 0) {
notEmpty.await();
}
return extract();
} catch (Exception e) {
e.printStackTrace();
return 0;
} finally {
System.out.println(Thread.currentThread().getName() +": released lock for take()");
lock.unlock();
}
}
private int extract() {
int index = count <= 0 ? 0 : count - 1;
Integer integer = list.get(index);
list.remove(index);
count--;
list.clear();
notFull.signal();
return integer;
}
public void put(int value) {
try {
lock.lock();
System.out.println(Thread.currentThread().getName() +": acquired lock in put()");
while (!list.isEmpty()) {
notFull.await();
}
Thread.sleep(3000); // let's assume it takes 3 secs to add value
insert(value);
} catch (Exception e) {
e.printStackTrace();
} finally {
System.out.println(Thread.currentThread().getName() +": released lock for put()");
lock.unlock();
}
}
private void insert(int value) {
list.add(value);
count = list.size();
notEmpty.signal();
}
}
我在控制台中收到此结果:
pool-1-thread-1: acquired lock in put()
pool-1-thread-1: released lock for put()
pool-1-thread-1: acquired lock in put() - this guy has taken the lock
pool-1-thread-2: acquired lock in take() - that guy has taken the lock as well! wtf?
pool-1-thread-2: released lock for take()
Value = 0
pool-1-thread-2: acquired lock in take()
pool-1-thread-1: released lock for put()
pool-1-thread-1: acquired lock in put()
pool-1-thread-2: released lock for take()
Value = 1
pool-1-thread-1: released lock for put()
pool-1-thread-1: acquired lock in put()
pool-1-thread-2: acquired lock in take()
pool-1-thread-2: released lock for take()
Value = 2
...
我怀疑这是因为我在循环中使用的条件,但逻辑上无法理解为什么会发生这种情况。 感谢您的解释。谢谢!
答案 0 :(得分:1)
当您从某个锁定获得的await()
上调用Condition
时,您的线程会释放锁定并停止,直到从另一个线程通知Condition
(通过{{ 1}}或signal()
)。
因此,您的线程1获取锁,但随后调用signalAll()
并切换到等待模式,释放最近由线程2获取的锁。当线程2完成时,它通知线程1(通过调用await()
)并释放锁。锁1会立即被线程1重新获取,它会唤醒并继续执行其工作并最终释放锁。
然后他们以稍微不同的顺序重复它。
答案 1 :(得分:1)
查看条件文档http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/locks/Condition.html
您需要了解的是条件(等待,信号)就像同步(等待,通知)。调用await()
将释放对锁定的保留。 (你仍然需要致电lock.unlock()
tho)。函数put()
等待take()
触发notFull.signal()
,take()
等待put()
触发notEmpty.signal()
。锁定最终会直接在await()
来电同步,而不会像您习惯的那样lock.lock()
同步。
这是记录在案的例子:
class BoundedBuffer {
final Lock lock = new ReentrantLock();
final Condition notFull = lock.newCondition();
final Condition notEmpty = lock.newCondition();
final Object[] items = new Object[100];
int putptr, takeptr, count;
public void put(Object x) throws InterruptedException {
lock.lock();
try {
while (count == items.length)
notFull.await();
items[putptr] = x;
if (++putptr == items.length) putptr = 0;
++count;
notEmpty.signal();
} finally {
lock.unlock();
}
}
public Object take() throws InterruptedException {
lock.lock();
try {
while (count == 0)
notEmpty.await();
Object x = items[takeptr];
if (++takeptr == items.length) takeptr = 0;
--count;
notFull.signal();
return x;
} finally {
lock.unlock();
}
}
}