JUC ReentrantLock 分析

基本介绍

ReentrantLock,可重入锁,基于AQS实现的互斥锁,在互斥锁之上支持可重入。可重入的意思是,同一个线程可以多次调用lock方法,而不会导致自己等待自己锁的释放。根据内部实现,分为公平性可重入锁和非公平性可重入锁。由构造函数来指定其公平性,默认使用非公平性实现。ReentrantLock实现了Lock接口,实现的接口如下:

public interface Lock {
    // 尝试获取锁,获取不到则阻塞等待,不响应中断
    void lock();
    // 尝试获取锁,获取不到则阻塞等待,响应中断
    void lockInterruptibly() throws InterruptedException;
    // 尝试获取锁,立即返回,获取成功返回true,失败则返回false
    boolean tryLock();
    // 超时获取锁,获取到则返回,获取不到知道超时时间过,返回false
    boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
    // 释放锁,需要在持有锁的线程中调用,否则会抛IllegalMonitorStateException,一般放到finally块中执行,确保在碰到任何异常,都正确能释放锁
    void unlock();
    // 新建一个同步等待条件
    Condition newCondition();
}

示例使用

public class Counter {

    private volatile int value = 0;
    private Lock lock = new ReentrantLock();

    public int inc() {
        lock.lock();
        try {
            ++value;
        } finllay {
            lock.unlock();
        }
    }

}

上面的例子仅作使用示例使用,在开发中常规的多线程并发计数器直接用基于CAS实现的atomic类即可(AtomicInteger,AtomicLong)

Lock vs Synchronized

synchronized的实现在底层,它的使用比较简单,基于方法或者基于方法块定义同步,可基于对象,class类来进行锁定;但synchronized无法实现超时锁,也不支持中断。而JUC中Lock的实现,可以实现超时获取锁(获取成功立刻返回,获取失败,根据超时时间返回),tryLock尝试获取锁(无论获取成功或者失败,都立马返回),锁等待过程支持中断。

源码分析

ReentrantLock实现了Lock接口,通过内部类Sync实现AQS提供的独占式获取锁方法和释放锁方法,来实现独占锁的功能,同时还区分公平性实现和非公平性实现:

public class ReentrantLock implements Lock, java.io.Serializable {

   private final Sync sync;

   abstract static class Sync extends AbstractQueuedSynchronizer {
        ...
   }

   // 非公平可重入锁实现
   static final class NonfairSync extends Sync {

       ...
   }

   // 公平性可重入锁实现
   static final class FairSync extends Sync {

   }

   ...
}

前面说到,ReentrantLock分为公平性可重入锁和非公平性可重入锁,通过其构造函数来指定公平性,默认使用的是非公平性实现。


public ReentrantLock() {
    sync = new NonfairSync();
}

public ReentrantLock(boolean fair) {
    sync = fair ? new FairSync() : new NonfairSync();
}

锁的公平性与非公平性获取

非公平获取体现在两个地方:

1)在NonfairSync中的lock方法:

final void lock() {
    if (compareAndSetState(0, 1))
        setExclusiveOwnerThread(Thread.currentThread());
    else
        acquire(1);
}

2)在Sync的nonfairTryAcquire方法,这是由上面的lock方法中的acquire方法衍生调用的:

Tip: acquire会调用tryAcquire方法,而NonfairSync的tryAcquire方法里面又调用了Sync中的的nonfairTryAcquire方法

final boolean nonfairTryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    int c = getState();
    if (c == 0) {
        if (compareAndSetState(0, acquires)) {
            setExclusiveOwnerThread(current);
            return true;
        }
    }
    else if (current == getExclusiveOwnerThread()) {
        int nextc = c + acquires;
        if (nextc < 0) // overflow
            throw new Error("Maximum lock count exceeded");
        setState(nextc);
        return true;
    }
    return false;
}

这里1)和2)的非公平性是说,任何线程,进入这段代码,先尝试CAS抢占锁(对AQS来说,指抢占同步状态state),而不用去理会AQS同步队列中是否有其他线程先于这些线程在等待获取独占锁。

基于上面的描述,我们就可以想象公平性的实现,实际上就是要先判断AQS同步队列是否还有其他线程在等待,FIFO的思想,先到的先尝试获取锁,这不就是公平的么,看代码,在FairSync类的tryAcquire方法中:


final void lock() {
    acquire(1);
}

protected final boolean tryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    int c = getState();
    if (c == 0) {
        // 公平性获取的体现
        if (!hasQueuedPredecessors() &&
            compareAndSetState(0, acquires)) {
            setExclusiveOwnerThread(current);
            return true;
        }
    }
    else if (current == getExclusiveOwnerThread()) {
        int nextc = c + acquires;
        if (nextc < 0)
            throw new Error("Maximum lock count exceeded");
        setState(nextc);
        return true;
    }
    return false;
}

注意到上面,当同步状态为0时候,优先判断是否同步队列里面有线程在等待,若有则当前线程不能抢占修改同步状态,必须失败(返回false),然后让AQS将它加到同步队列尾部。

锁的释放

公平性和非公平性的释放保持一致,都在Sync的tryRelease方法里面:

protected final boolean tryRelease(int releases) {
    int c = getState() - releases;
    if (Thread.currentThread() != getExclusiveOwnerThread())
        throw new IllegalMonitorStateException();
    boolean free = false;
    if (c == 0) {
        free = true;
        setExclusiveOwnerThread(null);
    }
    setState(c);
    return free;
}

支持中断的锁获取

直接调用AQS提供的acquireInterruptibly方法,而该方法会调用tryAcquire方法,实现由上面所述,分公平性和非公平性:

public void lockInterruptibly() throws InterruptedException {
    sync.acquireInterruptibly(1);
}

超时获取锁

直接调用AQS提供的tryAcquireNanos方法,该方法会调用tryAcquire方法,实现由上面所述,分公平性和非公平性:

public boolean tryLock(long timeout, TimeUnit unit)
        throws InterruptedException {
    return sync.tryAcquireNanos(1, unit.toNanos(timeout));
}

tryLock使用非公平实现

public boolean tryLock() {
    return sync.nonfairTryAcquire(1);
}

newCondition

通过newCondition()方法来构造一个Condition,一个Condition代表一个条件,当获取锁的情况下,可使用Condition来实现类似Object的wait/notify机制

eg:

private Lock lock = new ReentrantLock();
private Condition cond = lock.newCondition();

public void thread1() {
    lock.lock();
    try {
        while (!conditionMatch()) {
            cond.await();
        }
    } finally {
        lock.unlock();
    }
}

public void thread2() {
    lock.lock();
    try {
        cond.signalAll();
    } finally {
        lock.unlock();
    }
}

你可能感兴趣的:(java.concurrent,碎片化学JUC)