美文网首页
JUC系列 - ReentrantLock源码解析

JUC系列 - ReentrantLock源码解析

作者: FX_SKY | 来源:发表于2017-03-28 23:21 被阅读62次

    ReentrantLock,可重入锁,是一种递归无阻塞的同步机制。它可以等同于synchronized的使用,但是ReentrantLock提供了比synchronized更强大、灵活的锁机制,可以减少死锁发生的概率。

    A reentrant mutual exclusion Lock with the same basic behavior and semantics as the implicit monitor lock accessed using synchronized methods and statements, but with extended capabilities.
    A ReentrantLock is owned by the thread last successfully locking, but not yet unlocking it. A thread invoking lock will return, successfully acquiring the lock, when the lock is not owned by another thread. The method will return immediately if the current thread already owns the lock. This can be checked using methods isHeldByCurrentThread, and getHoldCount.

    本文将结合 **JDK1.7 ** 源码来分析java.util.concurrent.locks.ReentrantLock内部实现原理。

    首先,看看ReentrantLock 的构造方法:

        public class ReentrantLock implements Lock, java.io.Serializable {
            private static final long serialVersionUID = 7373984872572414699L;
            
            private final Sync sync;
    
            /**
             * 默认构造方法
             */
            public ReentrantLock() {
                sync = new NonfairSync();
            }
    
            /**
             * 带fair参数的构造方法
             */
            public ReentrantLock(boolean fair) {
                sync = fair ? new FairSync() : new NonfairSync();
            }
        }
    

    Sync为ReentrantLock里面的一个内部类,它继承AQS(AbstractQueuedSynchronizer),它有两个子类:公平锁FairSync和非公平锁NonfairSync。

    获取锁

    ReentrantLock的lock方法如下:

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

    下面我们看非公平锁(NonfairSync)的lock()方法:

        final void lock() {
            if (compareAndSetState(0, 1))   //尝试获取锁
                setExclusiveOwnerThread(Thread.currentThread());
            else    //获取锁失败
                acquire(1);
        }
    

    首先会第一次尝试快速获取锁,如果获取失败,则调用acquire(int arg)方法,该方法定义在AQS中,如下:

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

    这个方法首先调用tryAcquire(int arg)方法,在AQS中讲述过,tryAcquire(int arg)需要自定义同步组件提供实现,非公平锁实现如下:

        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) {   //state == 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;
        }
    

    首先判断同步状态state 是否为0,如果为0表示该锁还没有被线程持有,直接通过CAS获取同步状态,CAS成功则返回true。如果state不为0,则判断当前线程是否为获取锁的线程,如果是则获取锁,成功返回true。

    释放锁

    获取同步锁后,使用完毕则需要释放锁,ReentrantLock提供了unlock释放锁:

        public void unlock() {
            sync.release(1);
        }
    

    unlock方法内部调用了Sync的release(int arg)释放锁,release(int arg)是在AQS中定义的:

        public final boolean release(int arg) {
            if (tryRelease(arg)) {
                Node h = head;
                if (h != null && h.waitStatus != 0)
                    unparkSuccessor(h);
                return true;
            }
            return false;
        }
    

    与获取同步状态的acquire(int arg)方法相似,释放同步状态的tryRelease(int arg)同样是调用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;
        }
    

    公平锁与非公平锁

    公平锁与非公平锁的区别在于获取锁的时候是否按照FIFO的顺序来。释放锁不存在公平性和非公平性,上面以非公平锁为例,下面我们来看看公平锁(FairSync)的tryAcquire(int arg):

        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() {
            
            Node t = tail; //尾节点
            Node h = head;  //头节点
            Node s;
            return h != t &&
                ((s = h.next) == null || s.thread != Thread.currentThread());
        }
    

    该方法主要做一件事情:主要是判断当前线程是否位于CLH同步队列中的第一个。如果是则返回true,否则返回false。

    ReentrantLock与synchronized的区别

    ReentrantLock在加锁和内存上提供的语义与内置锁相同,区别在于:

    • ReentrantLock提供了一些其他功能,具备更强的扩展性,包括:定时的锁等待,可中断的锁等候,公平性。
    • ReentrantLock提供了条件(Condition),对线程的等待、唤醒操作更加详细和灵活。

    相关文章

      网友评论

          本文标题:JUC系列 - ReentrantLock源码解析

          本文链接:https://www.haomeiwen.com/subject/paisottx.html