ReentrantLock解析
ReentrantLock基于AbstractQueuedSynchronizer提供了一种独占锁的实现,分为公平锁和非公平锁。本文基于一个简单Producer-Consumer实现来分析一种运行场景的流程。
public 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();
}
}
public static void main(String... s) throws InterruptedException {
BoundedBuffer buffer = new BoundedBuffer();
new Thread(new BufferProducer(buffer),"ProducerThread").start();
new Thread(new BufferConsumer(buffer),"ConsumerThread").start();
}
}
public class BufferProducer implements Runnable{
private BoundedBuffer queue;
public BufferProducer(BoundedBuffer queue) {
this.queue = queue;
}
@Override
public void run() {
try{
System.out.println("Running in Thread:" + Thread.currentThread().getName());
queue.put("testing producer");
} catch(InterruptedException ee){
ee.printStackTrace();
}
}
}
public class BufferConsumer implements Runnable{
private BoundedBuffer queue;
public BufferConsumer(BoundedBuffer queue) {
this.queue = queue;
}
@Override
public void run() {
try{
Object test = queue.take();
System.out.println(test.toString());
System.out.println("Running in Thread:" + Thread.currentThread().getName());
} catch(InterruptedException ee){
ee.printStackTrace();
}
}
}
选择的场景是:开始运行的时候公用的queue是空的,ConsumerThread先获取独占锁,然后ProducerThread试图获取锁:
- ConsumerThread先获取独占,AQS中相关变量的状态:state=1,sync queue为空。
- ProducerThread试图获取锁,由于独占锁已经被占,获取不到它,被阻塞,往sync queue加入节点。AQS中相关变量的状态:state=1,sync queue包含2个节点,头结点是表示占有该资源的线程(独占模式)。 ProducerThread在AQS.acquireQueued调用parkAndCheckInterrupt()被阻塞。
- 由于公用的queue是空的,ConsumerThread调用notEmpty.await(),释放独占锁,ConsumerThread被阻塞。Condition.await():往条件队列中添加结点,调用AQS.fullyRelease释放独占锁,同时唤醒ProducerThread;AQS中相关变量的状态:state=0,sync queue包含2个节点
- ProducerThread被唤醒后,从AQS.acquireQueued被阻塞的地方开始执行,尝试获取锁成功,调用setHead()修改头结点,开始执行put()中的逻辑;AQS中相关变量的状态:state=1,sync queue包含1个节点
- ProducerThread调用notEmpty.signal(),把条件队列中结点transfer到sync queue中;AQS中相关变量的状态:state=1,sync queue包含2个节点
- ProducerThread调用lock.unlock释放锁,同时唤醒ConsumerThread;AQS中相关变量的状态:state=0,sync queue包含2个节点
- ConsumerThread被唤醒后,从Condition.await()被阻塞处开始执行,调用AQS.acquireQueued尝试获取锁成功,调用setHead()修改头结点,开始执行take()中业务逻辑;AQS中相关变量的状态:state=1,sync queue包含1个节点
- ConsumerThread调用lock.unlock释放锁,AQS中相关变量的状态:state=0,sync queue包含1个节点