昨天看了PriorityBlockingQueue,里面有一个Condition notEmpty变量,觉得挺奇怪的,今天,翻一翻API,了解一下。
目录
1.Condition是什么
Condition实际上就是将Ojbect对象的监视器(wait,notity)功能抽象出来了,从而允许形成多个等待队列。
之前的使用Object的wait方法,则将当前线程阻塞到该object的等待队列上。Condition也是为了实现类似的功能,在await时,释放当前拥有的锁,在signal时,唤醒某个线程(也是不能确定是那个线程)
2.Why Condition
可以形成多个不同条件的和锁关联的等待队列,从而更加灵活。对比Object的wait,notify只能形成一个等待队列(关于这个说明,见下面的例子)
3.例子
一个有界缓冲区的例子:当缓冲区满、空时,阻塞相应的放、取线程。
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();
}
}
}
使用Condition的好处是在一把锁上,形成两个等待队列。
有人又要问了,这里用了两个Condition,如果我用两个Ojbect对象,不是也能达到形成两个等待队列的效果么?
关于这个问题,我的理解是这样的:
a:你用两个object对象,是能形成两个等待队列,但是这两个等待队列是没有关系的。
b:你要调用object的wait,notify方法,你只能结合synchronzied使用,且你必须先要获取这些对象的锁,才能使用,这非常的烦琐。先不说不能使用lock带来的好处,先看看这样的代码可能是什么样子:
因为你要保护存储数据的items字段,保证只能有一个线程能够访问,则相应的方法就需要用synchronzed修饰(或是右创建一个obect对象,然后,用它来保护items对象)。另外,你需要操作notfull,notempty两个等待队列,那么需要首先获得这两把锁。
final Object notFull = new Object();
final Object notEmpty = new Object();
final Object[] items = new Object[100];
int putptr, takeptr, count;
synchronized public void put(Object x) throws InterruptedException {
synchronized(notFull)
{
synchronized(notEmpty)
{
while (count == items.length)
notFull.wait();
items[putptr] = x;
if (++putptr == items.length) putptr = 0;
++count;
notEmpty.notify();
}
}
}
分析一下:如果B一开始去取,当前缓冲区为空,失败,等待在notEmpty上。然后A线程调用了put,没有问题(B已经释放了锁),缓冲区不为空了,唤醒B。接着A一直调用put,直到缓存区满了,这时A会阻塞在notfull队列上,然后释放已经获得的锁。。。。
小节一下:这下比较清楚Condition的优势了吧,根据一把锁,形成多个等待队列。而Object的方法是一个锁,一个队列,如果要形成多个队列,则要形成多把锁。且Ojbect是和Synchronized结合使用的。
4.结合PriorityBlockingQueue看一眼用法
构造方法中创建相应的锁和condition对象
public PriorityBlockingQueue(int initialCapacity,
Comparator<? super E> comparator)
{
if (initialCapacity < 1)
throw new IllegalArgumentException();
this.lock = new ReentrantLock();
this.notEmpty = lock.newCondition();
this.comparator = comparator;
this.queue = new Object[initialCapacity];
}
在插入后 调用:notEmpty.signal();
在取的时候调用: while ( (result = dequeue()) == null)
notEmpty.await();
(因为该queue是无界的,在插入时,是永远可以的)
5.总结
a:和lock绑定,形成多个等待队列(只能和lock联合使用,通过lock.newCondition方法生成实例对象)
b:要写好一篇好不容易,以前觉得只要打打字就完了,现在发现我找了上面的按钮半天,也没有发现有个字体颜色的菜单,郁闷啊。