本篇总结Lock体系下的Condition的实现原理,以及它的await和signal等待/通知机制 ~
之前我们学到 Object的wait和notify/notifyAll
是与对象监视器monitor配合完成线程间的等待/通知机制,而Condition与Lock配合
完成等待通知机制,前者是java底层级别,后者是语言级别,具有 更高的可控制性和扩展性。
除了在使用方式上有不同之外,在功能特性上也有很大不同:
1 Condition支持不响应中断,Object方式不支持;
2 Condition支持多个等待队列(new多个Condition对象),而Object方式只支持一个;
3 Condition支持超时时间设置,Object不支持。
通过参照Object的wait和notify/notifyAll方法,Condition提供的方法如下:
void await() throws InterruptedException:当前线程进入等待态,若其他线程调用condition的signal或signalAll方法并且当前线程获取Lock,则从await方法返回;若在等待状态中被中断就抛出异常。
long awaitNanos(long nanosTimeout):当前线程进入等待状态知道被通知,中断或超时。
boolean await(long time,TimeUnit unit)throws InterruptedException:同上,支持自定义时间单位。
boolean awaitUntil(Date deadline)throws InterruptedException:当前线程进入等待状态直到被通知,中断或到了某个时间。
void signal():唤醒一个等待在condition上的线程,将该线程从等待队列中转移到同步队列中,若在同步队列中能竞争到Lock则可从等待方法中返回。
void signalAll():能唤醒所有等待在condition上的线程。
lock.newcondition()
创建一个condition对象,此方法实际上会new一个ConditionObject对象
,而ConditionObject类是AQS的一个内部类。private transient Node firstWaiter;
private transient Node lastWaiter;
Node nextWaiter;
condition.await()
方法的线程都会加入其中,并且线程状态转换为等待状态。lock.newcondition()
方法创建多个condition对象,也就是一个lock可持有多个等待队列。有界队列:当队列为空时,队列的获取(删除)操作将会阻塞获取(删除)线程,直到队列中有新增元素;当队列已满时,队列的插入操作将会阻塞插入线程,直到队列出现空位。
应用Condition实现有界队列如下:
public class BoundedQueue<T> {
private Object[] items;
// 队列中当前元素个数
private int count;
private Lock lock = new ReentrantLock();
private Condition empty = lock.newCondition();
private Condition full = lock.newCondition();
public BoundedQueue(int size) {
items = new Object[size];
}
// 添加元素方法,如果当前队列已满,则添加线程进入等待状态,直到有空位被唤醒
public void add(T t,int addIndex) throws InterruptedException {
lock.lock();
try {
// 当前队列已满,添加线程进入等待状态
while (count == items.length) {
full.await();
}
items[addIndex] = t;
count++;
empty.signal();
}finally {
lock.unlock();
}
}
// 删除元素方法,如果当前队列为空,则移除线程进入等待状态直到队列不为空时被唤醒
public T remove(int removeIndex) throws InterruptedException {
lock.lock();
try {
// 当队列为空时,移除线程进入等待状态
while (count == 0) {
empty.await();
}
Object x = items[removeIndex];
count--;
full.signal();
return (T) x;
}finally {
lock.unlock();
}
}
}
源码如下:
public final void await() throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
// 1.将当前线程包装为Node,尾插到等待队列中
Node node = addConditionWaiter();
// 2.释放当前线程所占用的lock,释放后会唤醒同步队列中的下一个节点
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) // clean up if cancelled
unlinkCancelledWaiters();
// 处理中断状况
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);
}
condition.await()
方法后会使当前获取lock的线程进入到等待队列,直至被signal/signalAll后使当前线程从等待队列中移至到同步队列中,直到获得lock后才会从await方法返回,或在等待时被中断做中断处理。addConditionWaiter()
将当前线程用尾插的方式将封装的Node插入到等待队列中。该方法源码如下:private Node addConditionWaiter() {
Node t = lastWaiter;
if (t != null && t.waitStatus != Node.CONDITION) {
unlinkCancelledWaiters();
t = lastWaiter;
}
// 将当前线程包装为Node
Node node = new Node(Thread.currentThread(), Node.CONDITION);
if (t == null)
firstWaiter = node;
else
// 尾插入等待队列
t.nextWaiter = node;
lastWaiter = node;
return node;
}
fullyRelease()
方法使当前线程释放lock,源码如下:final int fullyRelease(Node node) {
boolean failed = true;
try {
int savedState = getState();
// 调用AQS的释放同步状态方法release()
if (release(savedState)) {
// 成功释放锁状态
failed = false;
return savedState;
} else {
// 释放同步状态失败抛出异常
throw new IllegalMonitorStateException();
}
} finally {
// 释放同步状态失败后将当前节点状态置为取消状态
if (failed)
node.waitStatus = Node.CANCELLED;
}
}
上述代码调用AQS的模板方法release方法释放AQS的同步状态并且唤醒在同步队列中头结点的后继节点引用的线程。若释放成功则正常返回,若释放失败就抛出异常。
3 当前线程被中断或调用condition.signal/signalAll()
方法,使当前节点移动到同步队列,这是退出await方法的前提。当退出while循环后就会调用acquireQueued(node,savedState)
,在自旋过程不断尝试获取同步状态,直至成功,即必须是已经获得了condition引用(关联)的lock。
public final void signal() {
// 当前线程是否已经获取lock
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;
// transferForSignal方法对头结点做真正的处理
} while (!transferForSignal(first) && (first = firstWaiter) != null);
}
final boolean transferForSignal(Node node) {
// 首先将节点状态更新为0
if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
return false;
// 将节点使用enq方法尾插到同步队列中
Node p = enq(node);
int ws = p.waitStatus;
if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
LockSupport.unpark(node.thread);
return true;
}
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
class Goods {
// 商品名称
private String name;
// 当前商品数量
private int count;
// 商品最大数量
private int maxCount;
public Goods(int maxCount) {
this.maxCount = maxCount;
}
private Lock lock = new ReentrantLock();
private Condition consumer = lock.newCondition();
private Condition producer = lock.newCondition();
// 生产方法
public void setGoods(String name) {
lock.lock();
try {
// 当商品数量达到了最大值时阻塞生产者线程
while (count==maxCount) {
System.out.println(Thread.currentThread().getName()+"商品数量已达最大,等待消费者消费");
producer.await();
}
Thread.sleep(200);
// 生产商品
this.name = name;
count++;
System.out.println(Thread.currentThread().getName()+"生产"+toString());
// 唤醒消费者线程
consumer.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
// 消费方法
public void getGoods() {
lock.lock();
try {
// 商品数为0时阻塞消费者线程
while (count == 0) {
System.out.println(Thread.currentThread().getName()+"商品已被消费完,等待生产者生产");
consumer.await();
}
Thread.sleep(200);
// 消费商品
count--;
System.out.println(Thread.currentThread().getName()+"消费"+toString());
// 唤醒生产者线程
producer.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
@Override
public String toString() {
return "Goods{" +"name='" + name + '\'' +", count=" + count +'}';
}
}
class Producer implements Runnable {
private Goods goods;
public Producer(Goods goods) {
this.goods = goods;
}
@Override
public void run() {
while (true) {
this.goods.setGoods("纪梵希小羊皮口红套装");
}
}
}
class Consumer implements Runnable {
private Goods goods;
public Consumer(Goods goods) {
this.goods = goods;
}
@Override
public void run() {
while (true) {
this.goods.getGoods();
}
}
}
public class Test {
public static void main(String[] args) {
List<Thread> list = new ArrayList<>();
Goods goods = new Goods(10);
Producer producer = new Producer(goods);
Consumer consumer = new Consumer(goods);
// 创建10个消费者线程
for (int i = 0;i < 10;i++) {
Thread thread = new Thread(consumer,"消费者"+i);
list.add(thread);
}
// 创建5个生产者线程
for (int i = 0;i < 5;i++) {
Thread thread = new Thread(producer,"生产者"+i);
list.add(thread);
}
for (Thread th : list) {
th.start();
}
}
}