• 并发和多线程(十六)ReentrantLock源码解析 Diamond



    前面刚学习了AQS的基本原理,主要通过两个队列实现功能(同步队列+等待队列,前者是双向链表,实现加锁和解锁,后者是单向链表,用做同步协作,阻塞、唤醒),正好可以趁热打铁,了解一下ReentrantLock的源码,有了AQS的基础,阅读ReentrantLock的源码是非常简单的,如果没有了解AQS原理的同学,可以参考:
    AbstractQueuedSynchronizer源码(上)--排他锁
    AbstractQueuedSynchronizer源码(下)--共享锁和Condition条件队列
    我们都知道ReentrantLock的底层就是通过AQS实现,和Synchronized一样都是可重入锁,同时也是排他锁,所以ReentrantLock只能一个线程获取锁,但是线程可以获取当前资源的多重锁,对应锁数量+1,每次释放-1,直到等于0,才可以被其他线程竞争获取锁,有点引用计数法的意思。

    类定义:

    public class ReentrantLock implements Lock, java.io.Serializable {
        private final Sync sync;
        //默认构造函数,得到的事非公平锁
        public ReentrantLock() {
            sync = new NonfairSync();
        }
        //支持传入fair参数,得到是否公平锁
        public ReentrantLock(boolean fair) {
            sync = fair ? new FairSync() : new NonfairSync();
        }
    }
    

    ReentrantLock实现Lock接口,序列化,这里并没有实现AQS接口,而是通过内部类Sync实现,然后Sync作为成员变量,实现各种方法,Sync我们会在下面说。先来了解一个Lock接口。

    Lock:

    public interface Lock {
    
        //加锁,如果获取不到锁会到同步队列中阻塞
        void lock();
        //获取可中断锁
        void lockInterruptibly() throws InterruptedException;
        //尝试获取锁,成功返回true,否则返回false
        boolean tryLock();
        //带有超时时间tryLock
        boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
        //解锁
        void unlock();
        //得到condition实例
        Condition newCondition();
    }
    

    这些方法在ReentrantLock中实现,主要通过静态内部类Sync实现,也是Sync实现了AQS。。

    Sync:

    Sync并没有实现加锁,而是通过两个子类FairSync和NonfairSync实现,FairSync和NonfairSync对应着公平锁和非公平锁实现方式。

    abstract static class Sync extends AbstractQueuedSynchronizer {
        private static final long serialVersionUID = -5179523762034025860L;
    
        //加锁,抽象方法,子类实现
        abstract void lock();
    
        //尝试获取费公平锁,有两个地方调用,可自行通过Ctrl+Alt+H查看
        final boolean nonfairTryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            //如果当前没有现成持有锁
            if (c == 0) {
                //CAS将状态从0设置为参数值
                if (compareAndSetState(0, acquires)) {
                    //将当前线程设置到AbstractOwnableSynchronizer中
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            //如果当前锁被持有,当前线程为持有锁线程
            else if (current == getExclusiveOwnerThread()) {
                //ReentrantLock为可重入锁,可以对资源多次加锁
                int nextc = c + acquires;
                //超过integer的最大值,抛出异常
                if (nextc < 0) // overflow
                    throw new Error("Maximum lock count exceeded");
                //直接设置state,返回true
                setState(nextc);
                return true;
            }
            //如果持有锁的线程不是当前线程,返回false,获取锁失败
            return false;
        }
        
        //尝试释放锁
        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;
        }
    
    //后面的方法比较简单,不就说了
    ...
    }
    

    tryRelease():

    上面看了tryRelease代码,内容比较简单,state为0表示表示release成功,如果线程对资源加锁多次,那么解锁也需要相应的次数,才能release,然后被其他线程竞争这把锁。
    PS:释放锁tryRelease是不区分共享锁和排他锁的。

    FairSync

    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) {
                //判断当前线程是否为同步队列head节点的后驱节点的线程,如果是可以获取锁,false阻塞等待,这是实现公平锁的关键,实现线程在队列的先入先出
                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;
        }
    }
    

    FairSync

    static final class NonfairSync extends Sync {
        private static final long serialVersionUID = 7316153563782823691L;
        //非公平锁加锁
        final void lock() {
            //CAS尝试将state从0设置为1,成功,设置持有锁线程,否则acquire获取锁
            if (compareAndSetState(0, 1))
                setExclusiveOwnerThread(Thread.currentThread());
            else
                acquire(1);
        }
    
        //这里调用了前面Sync定义的nonfairTryAcquire()
        protected final boolean tryAcquire(int acquires) {
            return nonfairTryAcquire(acquires);
        }
    }
    

    总结:
    对于加锁,解锁的流程,调用lock()unlock()的过程可以自己看下api,算是比较简单,也就不写代码了。ReentrantLock直接使用AQS整体实现非常简单,所以掌握AQS还是很有必要的,包括后面了解CountDownLatch、Semaphore实现都是很有必要的。

  • 相关阅读:
    mysql查看进程
    mysql case, if
    centos升级python2.7
    centos多版本python安装pip
    Python library not found: libpython2.7mu.so.1.0
    pip cannot confirm SSL certificate: SSL module is not available
    python: no module named bz2
    summary
    python生成可执行文件保护源码
    mysql 存储过程
  • 原文地址:https://www.cnblogs.com/huigelaile/p/15780381.html
Copyright © 2020-2023  润新知