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试图获取锁:

  1. ConsumerThread先获取独占,AQS中相关变量的状态:state=1,sync queue为空。
  2. ProducerThread试图获取锁,由于独占锁已经被占,获取不到它,被阻塞,往sync queue加入节点。AQS中相关变量的状态:state=1,sync queue包含2个节点,头结点是表示占有该资源的线程(独占模式)。 ProducerThread在AQS.acquireQueued调用parkAndCheckInterrupt()被阻塞。
  3. 由于公用的queue是空的,ConsumerThread调用notEmpty.await(),释放独占锁,ConsumerThread被阻塞。Condition.await():往条件队列中添加结点,调用AQS.fullyRelease释放独占锁,同时唤醒ProducerThread;AQS中相关变量的状态:state=0,sync queue包含2个节点
  4. ProducerThread被唤醒后,从AQS.acquireQueued被阻塞的地方开始执行,尝试获取锁成功,调用setHead()修改头结点,开始执行put()中的逻辑;AQS中相关变量的状态:state=1,sync queue包含1个节点
  5. ProducerThread调用notEmpty.signal(),把条件队列中结点transfer到sync queue中;AQS中相关变量的状态:state=1,sync queue包含2个节点
  6. ProducerThread调用lock.unlock释放锁,同时唤醒ConsumerThread;AQS中相关变量的状态:state=0,sync queue包含2个节点
  7. ConsumerThread被唤醒后,从Condition.await()被阻塞处开始执行,调用AQS.acquireQueued尝试获取锁成功,调用setHead()修改头结点,开始执行take()中业务逻辑;AQS中相关变量的状态:state=1,sync queue包含1个节点
  8. ConsumerThread调用lock.unlock释放锁,AQS中相关变量的状态:state=0,sync queue包含1个节点

ReentrantReadWriteLock参考

  1. Java并发包中的读写锁及其实现分析 2. JUC 可重入 读写锁 ReentrantReadWriteLock
  2. 深入浅出Java并发包—读写锁ReentrantReadWriteLock原理分析

results matching ""

    No results matching ""