了解ReentrantLock

了解ReentrantLock

ReetrantLock是一个可重入的独占锁,主要有两个特性,一个是支持公平锁和非公平锁,一个是可重入。
ReetrantLock实现依赖于AQS(AbstractQueuedSynchronizer)
ReetrantLock主要依靠AQS维护一个阻塞队列,多个线程对加锁时,失败则会进入阻塞队列。等待唤醒,重新尝试加锁。下图是其类图
20180226134553506-45bd7b55ef6b42dc9cc3d958d720a1e7

支持公平锁和非公平锁

  1. 公平锁:多个线程申请获取同一资源时,必须按照申请顺序,依次获取资源。
  2. 非公平锁:资源释放时,任何线程都有机会获得资源,而不管其申请顺序。
    具体原理分析

在new ReetrantLock对象的时候,可以指定其支持公平锁还是非公平锁

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


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

公平锁原理

FairSync继承Sync,而Sync继承AbstractQueuedSynchronizer。ReentrantLock调用lock方法,最终会调用sync的tryAcquire函数,获取资源。FairSync的tryAcquire函数,**当前线程只有在队列为空或者时队首节点的时候,才能获取资源,否则会被加入到阻塞队列中。**下面的hasQueuedPredecessors函数用于判断队列是否为空,或者当前元素是否为队首元素。

static final class FairSync extends Sync {
    private static final long serialVersionUID = -3000897897090466540L;

    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;
    }
}

hasQueuedPredecessors函数,如果h==t(队列为空),或者h!=t && s.thread == Thread.currentThread()(队首节点),则返回false,否则返回true

public final boolean hasQueuedPredecessors() {
        Node t = tail; // Read fields in reverse initialization order
        Node h = head;
        Node s;
        return h != t &&
            ((s = h.next) == null || s.thread != Thread.currentThread());
}

非公平锁原理

NoFairSync同样继承Sync,ReentrantLock调用lock方法,最终会调用sync的tryAcquire函数,获取资源。而NoFairSync的tryAcquire函数,会调用父类Sync的方法nofairTryAcquire函数。通过对比可以发现,如果资源释放时,新的线程会尝试CAS操作获取资源,而不管阻塞队列中时候有先于其申请的线程。

static final class NonfairSync extends Sync {
    private static final long serialVersionUID = 7316153563782823691L;
    final void lock() {
        if (compareAndSetState(0, 1))
            setExclusiveOwnerThread(Thread.currentThread());
        else
            acquire(1);
    }

    protected final boolean tryAcquire(int acquires) {
        return nonfairTryAcquire(acquires);
    }
}

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;
}

可重入性

可重入性:获取独占资源的线程,可以重复得获取该独占资源,不需要重复请求。

请求独占资源时,可重入性的体现

ReentrantLock在申请资源的时候,都会判断当前持有独占资源的线程是不是当前线程,如果是的话,只是简单得将state值加1.记录当前线程的重入次数。

 else if (current == getExclusiveOwnerThread()) {
     int nextc = c + acquires;
     if (nextc < 0)
     	throw new Error("Maximum lock count exceeded");
     setState(nextc);
     return true;
 }

释放资源时,可重入性的体现

ReentrantLock在释放资源的时候,都会调用tryRelease,只有state值为0的时候,才会释放资源。换句话说就是,重入多少次,就必须释放多少次

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;
}