Condition也是 AQS 中很重要的一块内容,可以先看段示例代码,这段代码应该来自于Doug Lea大大,可以在 javadoc 中的 condition 部分找到,其实大大原来写过基于 synchronized 实现的,后面我也贴下代码
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
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 的结构
public class ConditionObject implements Condition, java.io.Serializable {
private static final long serialVersionUID = 1173984872572414699L;
private transient Node firstWaiter;
private transient Node lastWaiter;
主要的就这么点,而且也复用了 AQS 阻塞队列或者大大叫 lock queue中同样的 Node 节点,只不过它没有使用其中的双向队列,也就是prev 和 next,而是在 Node 中的 nextWaiter,所以只是个单向的队列,没使用 next 其实还有个用处,后面会提到,看下结构的示意图
![]()
然后主要是看两个方法,await 和 signal,
先来看下 await
public final void await() throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
Node node = addConditionWaiter();
int savedState = fullyRelease(node);
int interruptMode = 0;
while (!isOnSyncQueue(node)) {
LockSupport.park(this);
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break;
}
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
interruptMode = REINTERRUPT;
if (node.nextWaiter != null)
unlinkCancelledWaiters();
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);
}
添加条件队列节点
private Node addConditionWaiter() {
Node t = lastWaiter;
if (t != null && t.waitStatus != Node.CONDITION) {
unlinkCancelledWaiters();
t = lastWaiter;
}
Node node = new Node(Thread.currentThread(), Node.CONDITION);
if (t == null)
firstWaiter = node;
else
t.nextWaiter = node;
lastWaiter = node;
return node;
}
清理取消的节点
private void unlinkCancelledWaiters() {
Node t = firstWaiter;
Node trail = null;
while (t != null) {
Node next = t.nextWaiter;
if (t.waitStatus != Node.CONDITION) {
t.nextWaiter = null;
if (trail == null)
firstWaiter = next;
else
trail.nextWaiter = next;
if (next == null)
lastWaiter = trail;
}
else
trail = t;
t = next;
}
}
完全释放锁
final int fullyRelease(Node node) {
boolean failed = true;
try {
int savedState = getState();
if (release(savedState)) {
failed = false;
return savedState;
} else {
throw new IllegalMonitorStateException();
}
} finally {
if (failed)
node.waitStatus = Node.CANCELLED;
}
}
判断是否在阻塞队列中
final boolean isOnSyncQueue(Node node) {
if (node.waitStatus == Node.CONDITION || node.prev == null)
return false;
if (node.next != null)
return true;
return findNodeFromTail(node);
}
private boolean findNodeFromTail(Node node) {
Node t = tail;
for (;;) {
if (t == node)
return true;
if (t == null)
return false;
t = t.prev;
}
}
await 的逻辑差不多就是这样子,主要的就是把自己包成一个 Node 节点,waitStatus 的状态是 CONDITION,挂在等待队列的最后,然后完全释放锁,park 等待
signal
public final void signal() {
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
Node first = firstWaiter;
if (first != null)
doSignal(first);
}
private void doSignal(Node first) {
do {
if ( (firstWaiter = first.nextWaiter) == null)
lastWaiter = null;
first.nextWaiter = null;
} while (!transferForSignal(first) &&
(first = firstWaiter) != null);
}
final boolean transferForSignal(Node node) {
if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
return false;
Node p = enq(node);
int ws = p.waitStatus;
if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
LockSupport.unpark(node.thread);
return true;
}
这里其实就是把 condition 等待队列的第一个未取消的节点入队到阻塞队列去争锁
附录
synchronized 版的 BoundedBuffer
package EDU.oswego.cs.dl.util.concurrent;
public class BoundedBuffer implements BoundedChannel {
protected final Object[] array_;
protected int takePtr_ = 0;
protected int putPtr_ = 0;
protected int usedSlots_ = 0;
protected int emptySlots_;
protected final Object putMonitor_ = new Object();
public BoundedBuffer(int capacity) throws IllegalArgumentException {
if (capacity <= 0) throw new IllegalArgumentException();
array_ = new Object[capacity];
emptySlots_ = capacity;
}
public BoundedBuffer() {
this(DefaultChannelCapacity.get());
}
public synchronized int size() { return usedSlots_; }
public int capacity() { return array_.length; }
protected void incEmptySlots() {
synchronized(putMonitor_) {
++emptySlots_;
putMonitor_.notify();
}
}
protected synchronized void incUsedSlots() {
++usedSlots_;
notify();
}
protected final void insert(Object x) {
--emptySlots_;
array_[putPtr_] = x;
if (++putPtr_ >= array_.length) putPtr_ = 0;
}
protected final Object extract() {
--usedSlots_;
Object old = array_[takePtr_];
array_[takePtr_] = null;
if (++takePtr_ >= array_.length) takePtr_ = 0;
return old;
}
public Object peek() {
synchronized(this) {
if (usedSlots_ > 0)
return array_[takePtr_];
else
return null;
}
}
public void put(Object x) throws InterruptedException {
if (x == null) throw new IllegalArgumentException();
if (Thread.interrupted()) throw new InterruptedException();
synchronized(putMonitor_) {
while (emptySlots_ <= 0) {
try { putMonitor_.wait(); }
catch (InterruptedException ex) {
putMonitor_.notify();
throw ex;
}
}
insert(x);
}
incUsedSlots();
}
public boolean offer(Object x, long msecs) throws InterruptedException {
if (x == null) throw new IllegalArgumentException();
if (Thread.interrupted()) throw new InterruptedException();
synchronized(putMonitor_) {
long start = (msecs <= 0)? 0 : System.currentTimeMillis();
long waitTime = msecs;
while (emptySlots_ <= 0) {
if (waitTime <= 0) return false;
try { putMonitor_.wait(waitTime); }
catch (InterruptedException ex) {
putMonitor_.notify();
throw ex;
}
waitTime = msecs - (System.currentTimeMillis() - start);
}
insert(x);
}
incUsedSlots();
return true;
}
public Object take() throws InterruptedException {
if (Thread.interrupted()) throw new InterruptedException();
Object old = null;
synchronized(this) {
while (usedSlots_ <= 0) {
try { wait(); }
catch (InterruptedException ex) {
notify();
throw ex;
}
}
old = extract();
}
incEmptySlots();
return old;
}
public Object poll(long msecs) throws InterruptedException {
if (Thread.interrupted()) throw new InterruptedException();
Object old = null;
synchronized(this) {
long start = (msecs <= 0)? 0 : System.currentTimeMillis();
long waitTime = msecs;
while (usedSlots_ <= 0) {
if (waitTime <= 0) return null;
try { wait(waitTime); }
catch (InterruptedException ex) {
notify();
throw ex;
}
waitTime = msecs - (System.currentTimeMillis() - start);
}
old = extract();
}
incEmptySlots();
return old;
}
}