代码改变世界

java并发包学习:1.ReentrantLock代码公平锁和非公平锁实现解析

2017-11-28 22:22  笔法春秋  阅读(475)  评论(0编辑  收藏  举报

 

java中ReentrantLock是一个功能比较简单的显式锁,也是一个比较好的学习java并发包的一个切入点,今天看了下ReentrantLock的代码,写个文章记录一下

先看一下ReentrantLock类的基本结构;

ReentrantLock实现了Lock接口:Lock接口定义了以下api:

void lock();
void lockInterruptibly() throws InterruptedException;
boolean tryLock();
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
void unlock();
Condition newCondition();

 

 

 

lock();的实现

在ReentrantLock中,它对lock的实现非常简单:

public void lock() {
        sync.lock();
    }

 

 

而sync是什么呢?它是AQS(AbstractQueuedSynchronizer)的一个实现

private final Sync sync;

    /**
     * Base of synchronization control for this lock. Subclassed
     * into fair and nonfair versions below. Uses AQS state to
     * represent the number of holds on the lock.
     */
    abstract static class Sync extends AbstractQueuedSynchronizer 

 

AbstractQueuedSynchronizer本身是一个可以用于构建锁或者其他相关同步装置的基础框架,通过继承来实现现大部分同步需求的基础,要详细了解AbstractQueuedSynchronizer,可以看http://ifeve.com/introduce-abstractqueuedsynchronizer/ f里面有非常详细的介绍

AbstractQueuedSynchronizer提供了四个待子类重写的方法,风别是:

方法名称 描述
protected boolean tryAcquire(int arg) 排它的获取这个状态。这个方法的实现需要查询当前状态是否允许获取,然后再进行获取(使用compareAndSetState来做)状态。
protected boolean tryRelease(int arg)  释放状态。
protected int tryAcquireShared(int arg) 共享的模式下获取状态。
protected boolean tryReleaseShared(int arg) 共享的模式下释放状态。
protected boolean isHeldExclusively() 在排它模式下,状态是否被占用。

 

同时Syn重写了AbstractQueuedSynchronizer的两个方法

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

        protected final boolean isHeldExclusively() {
            // While we must in general read state before owner,
            // we don't need to do so to check if current thread is owner
            return getExclusiveOwnerThread() == Thread.currentThread();
        }

 

而在ReentrantLock中,默认Syn是NonfairSync实例

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

NonfairSync本身是一个非公平锁,在本文中,就先拿NonfairSync来做分析

   static final class NonfairSync extends Sync {
        private static final long serialVersionUID = 7316153563782823691L;

        /**
         * Performs lock.  Try immediate barge, backing up to normal
         * acquire on failure.
         */
        final void lock() {
            if (compareAndSetState(0, 1))
                setExclusiveOwnerThread(Thread.currentThread());
            else
                acquire(1);
        }

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

大家注意到没有,在NonfairSync的实现中,它重写了AbstractQueuedSynchronizer中的tryAcquire函数,而tryAcquire的本身是一个用于获取排它锁的函数

先来看lock方法

  /**
         * Performs lock.  Try immediate barge, backing up to normal
         * acquire on failure.
         */
        final void lock() {
            if (compareAndSetState(0, 1))
                setExclusiveOwnerThread(Thread.currentThread());
            else
                acquire(1);
        }

这个方法很好理解,如果无线程持有当前锁,那么调用acquire函数

acquire是怎么做的呢?

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

它会根据tryAcquire(arg)判断是否满足获取锁的条件

而在NonfairSync,则重写了tryAcquire的逻辑,

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

 

 很清晰的逻辑,为了保证可重入性,实现里通过计数器和是否当前线程来判断是否能获取锁。

 而其非公平性的代码体现是在

/**
         * Performs lock.  Try immediate barge, backing up to normal
         * acquire on failure.
         */
        final void lock() {
            if (compareAndSetState(0, 1))
                setExclusiveOwnerThread(Thread.currentThread());

这块进行体现的,加入一条线程释放锁,同时另一条线程进入,则不通过SQR的阻塞队列来实现加锁,而是直接让其通过。


 

以上是非公平锁的lock实现,理解了非公平性加锁,再来看公平性加锁,就很容易了

以下是公平锁的初始函数:

 /**
     * Creates an instance of {@code ReentrantLock} with the
     * given fairness policy.
     *
     * @param fair {@code true} if this lock should use a fair ordering policy
     */
    public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
    }

 在这里,公平性锁是通过FairSync来实现的,来看看FairSync的代码

    /**
     * Sync object for fair locks
     */
    static final class FairSync extends Sync {
        private static final long serialVersionUID = -3000897897090466540L;

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

        /**
         * Fair version of tryAcquire.  Don't grant access unless
         * recursive call or no waiters or is first.
         */
        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里,且看实现

  public final boolean hasQueuedPredecessors() {
        // The correctness of this depends on head being initialized
        // before tail and on head.next being accurate if the current
        // thread is first in queue.
        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());
    }

它的返回逻辑分析一下:head不等于tail或者(队首的线程和当前线程不一致)

那么如果hasQueuedPredecessors返回true,那也就意味着当前线程不能获取锁;而如果是false,那么当前线程是AQS队列的列首,可以获取锁,并以此来实现公平锁。

在依赖AbstractQueuedSynchronizer的情况下实现并不复杂,大部分的底层阻塞逻辑其实是在AbstractQueuedSynchronizer里做的,下一篇文章将对AbstractQueuedSynchronizer进行分析。