ReentrantLock基于AQS的公平锁和非公平锁的实现区别

ReentrantLock锁的实现是基于AQS实现的,所以先简单说下AQS:

AQS是AbstractQueuedSynchronizer缩写,顾名思义:抽象的队列同步器,它是JUC里面许多同步工具类实现的核心

其实简单来说AQS有两个核心,一个是volatile修饰的int类型state,这个是记录处于等待中需要持有锁和正在持有锁的线程数量

/**
 * The synchronization state.
 */
private volatile int state;

第二个就是Node内部类,他是AQS里面FIFO双向队列节点的实现,他的一些属性如下:

static final class Node {
     volatile int waitStatus;//等待状态 volatile Node prev;//前驱节点 volatile Node next;//后继节点 volatile Thread thread;//申请同步状态的线程 Node nextWaiter;//等待节点的后继节点(后续出AQS详细篇再细讲) }

这种结构可以从任意的一个节点开始很方便的访问前驱和后继节点。每个Node由线程封装,当线程竞争失败后会加入到AQS队列中去。

基于这些再继续聊下ReentrantLock的公平和非公平锁的实现

ReentrantLock锁的实现基于AQS,如下sync抽象内部类:

abstract static class Sync extends AbstractQueuedSynchronizer{
  abstract void lock(); 
}

Sync的子类有两个:FairSync和NonfairSync(公平和非公平锁的具体实现类),两个锁的不同就在于两个方法的实现不同lock():加锁的方法,还有tryAcquire(int acquires):尝试持有锁的方法,获取成功返回true,失败返回false

看看两把锁的源码实现:

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

  

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

nonfairTryAcquire方法是在父类Sync中定义:

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

可以看见,NonfairSync在使用lock()加锁的时候就已经体现了非公平性了,因为lock()加锁的时候直接尝试使用CAS获取锁,如果获取到了就不会入等待队列,所以会有后来的线程先抢占到锁;

那如果没有抢占到锁呢?会走else的acquire(1)方法。

先看看acquire方法的定义:

public final void acquire(int arg) {
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }

这里会发现acquire调用了tryAcquire方法,而在上面两把锁的源码中各自重写了tryAcquire方法

这个方法中当getState()获取同步状态为0(没有线程持有锁)时候,继续使用用CAS获取锁

两个重写的方法唯一不同之处在于,公平锁加了hasQueuedPredecessors()方法:

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

这个方法主要是用来判断线程需不需要排队,因为队列是FIFO的,所以需要判断队列中有没有相关线程的节点已经在排队了。有则返回true表示线程需要排队,没有则返回false则表示线程无需排队。

而非公平锁就没有判断FIFO队列是否还有排队。

小结:

ReentrantLock中公平锁和非公平锁的实现主要有两处区别:

1.在lock()方法中,非公平锁一进来就尝试CAS获取锁,不会管等待队列里面是否有等待线程

2.在tryAcquire方法中,判断state等于0(没有线程持有锁的情况)后,公平锁会先调用hasQueuedPredecessors()方法判断FIFO队列是否有等待线程,没有才继续尝试获取锁,而非公平锁是直接CAS获取锁

值得注意的是众所周知ReentrantLock是可重入锁:

不管是公平锁还是非公平锁,他们重写的尝试抢占锁的tryAcquire方法里面都有这样一段代码

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

 这个是判断state不等于0(有线程持有锁或者等待线程时候)之后,继续判断当前持有锁的线程是不是等于当前申请锁的线程,如果等于,直接返回true并且state+1,所以当前线程不用等待继续持有锁;这就完成了可重入的特性!

Tags: