condition源码分析及基于condition实现阻塞队列
condition通过wait(),signal()实现线程间通信
public class ContidionDemo {
private List<String> bags = new ArrayList<>();
private Integer maxSize = 10;
Lock lock = new ReentrantLock();
private volatile Integer size = 0;
Condition full_condition = lock.newCondition();
Condition empty_condition = lock.newCondition();
public ContidionDemo(Integer maxSize) {
this.bags = new ArrayList<>(maxSize);
this.maxSize = maxSize;
}
public void put(String item) {
lock.lock();
//如果是队列满了,那就先阻塞
try {
if (size == maxSize) {
System.out.println("队列满了,先等一下-----");
full_condition.await();
}
size++;
bags.add(item);
empty_condition.signal();
System.out.println("插入到对队列中的数据" + item);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void get() {
lock.lock();
try {
if (size == 0) {
System.out.println("队列空了,先等一下-------");
empty_condition.await();
}
size--;
String item = bags.remove(0);
full_condition.signal();
System.out.println("取出的元素为:" + item);
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public static void main(String[] args) {
//假设该队列的最大值为10
ContidionDemo contidionDemo = new ContidionDemo(10);
Thread t1 = new Thread(() -> {
for (int i = 0; i < 10000; i++) {
String item = "item-" + i;
contidionDemo.put(item);
}
});
Thread t2 = new Thread(() -> {
for (int i = 0; i < 10000; i++) {
contidionDemo.get();
}
});
t1.start();
t2.start();
}
}
运行结果(部分):
队列满了,先等一下-----
取出的元素为:item-9886
取出的元素为:item-9887
取出的元素为:item-9888
取出的元素为:item-9889
取出的元素为:item-9890
取出的元素为:item-9891
取出的元素为:item-9892
取出的元素为:item-9893
取出的元素为:item-9894
取出的元素为:item-9895
队列空了,先等一下-------
插入到对队列中的数据item-9896
插入到对队列中的数据item-9897
插入到对队列中的数据item-9898
插入到对队列中的数据item-9899
插入到对队列中的数据item-9900
插入到对队列中的数据item-9901
插入到对队列中的数据item-9902
插入到对队列中的数据item-9903
插入到对队列中的数据item-9904
插入到对队列中的数据item-9905
队列满了,先等一下-----
取出的元素为:item-9896
取出的元素为:item-9897
取出的元素为:item-9898
取出的元素为:item-9899
取出的元素为:item-9900
取出的元素为:item-9901
取出的元素为:item-9902
取出的元素为:item-9903
取出的元素为:item-9904
取出的元素为:item-9905
队列空了,先等一下-------
插入到对队列中的数据item-9906
插入到对队列中的数据item-9907
插入到对队列中的数据item-9908
插入到对队列中的数据item-9909
插入到对队列中的数据item-9910
插入到对队列中的数据item-9911
插入到对队列中的数据item-9912
插入到对队列中的数据item-9913
取出的元素为:item-9906
取出的元素为:item-9907
取出的元素为:item-9908
取出的元素为:item-9909
取出的元素为:item-9910
取出的元素为:item-9911
取出的元素为:item-9912
取出的元素为:item-9913
队列空了,先等一下-------
插入到对队列中的数据item-9914
插入到对队列中的数据item-9915
插入到对队列中的数据item-9916
插入到对队列中的数据item-9917
插入到对队列中的数据item-9918
插入到对队列中的数据item-9919
插入到对队列中的数据item-9920
插入到对队列中的数据item-9921
插入到对队列中的数据item-9922
插入到对队列中的数据item-9923
队列满了,先等一下-----
取出的元素为:item-9914
取出的元素为:item-9915
取出的元素为:item-9916
取出的元素为:item-9917
取出的元素为:item-9918
取出的元素为:item-9919
取出的元素为:item-9920
取出的元素为:item-9921
取出的元素为:item-9922
取出的元素为:item-9923
队列空了,先等一下-------
以上代码实现了阻塞队列的功能。当队列满了以后,插入操作暂时阻塞。当队列空了之后,删除操作暂时阻塞。期间通过wait()和signal()实现线程间通信。
接来下我们分析下这两个方法的源代码,分别是如何实现线程的阻塞和唤醒的。
wait()
public final void await() throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
//添加到等待队列
Node node = addConditionWaiter();
//完全释放锁,这里要考虑到重入锁的可能性,返回值是当前线程重入锁的次数
int savedState = fullyRelease(node);
int interruptMode = 0;
//如果该锁没有在AQS的阻塞队列中,就阻塞她
while (!isOnSyncQueue(node)) {
LockSupport.park(this);//在被唤醒之前,会一直这里阻塞。
//判断该线程的唤醒是否是因为interrupt方法唤醒的。(线程唤醒之后,还会从这里执行)
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break;
}
//重新竞争锁
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
interruptMode = REINTERRUPT;
if (node.nextWaiter != null) // clean up if cancelled
unlinkCancelledWaiters();
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);
}
//将当前进程封装到一个Node中,添加到等待队列中
private Node addConditionWaiter() {
Node t = lastWaiter;
// If lastWaiter is cancelled, clean out.
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;
}
//完全释放锁
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 acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
failed = false;
return interrupted;
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
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 cannot change waitStatus, the node has been cancelled.
*/
if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
return false;
/*
* Splice onto queue and try to set waitStatus of predecessor to
* indicate that thread is (probably) waiting. If cancelled or
* attempt to set waitStatus fails, wake up to resync (in which
* case the waitStatus can be transiently and harmlessly wrong).
*/
//这里是把当前从等待队列中头部节点的保存到AQS队列
Node p = enq(node);
int ws = p.waitStatus;
if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
LockSupport.unpark(node.thread); //唤醒.
return true;
}