并发编程ReentrantLock实现原理和Synchroized区别

本文详细解析了ReentrantLock的实现原理,比较了公平锁与非公平锁在调度策略上的区别,以及它们在并发场景中的适用性。重点介绍了构造函数、公平锁和非公平锁的源码,并讨论了与`synchronized`的区别,如可重入性、公平性及控制方式的不同。
摘要由CSDN通过智能技术生成

ReentrantLock是Lcok默认实现方式之一,它是基于AQS(AbstractQueuedSynchronizer 队列同步器)实现的,它默认是通过非公平锁实现的,在它的内部有一个state的状态字段用于表示锁是否被占用,如果是0则表示未被占用,此时线程就可以把state改成1,并成功获得了锁,而其他未获得锁的线程只能去排队等待获取资源

首先来看下ReentrantLock的两个构造函数:

    public ReentrantLock() {
        sync = new NonfairSync();//非公平锁
    }
 
    public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
    }

无参的构造函数创建了一个非公平锁,用户也可以根据第二个构造方法,设置一个boolean类型的值,来决定是否使用公平锁来实现现成的调度。

看下公平锁源码:

//可以看到该方法就是以独占的方式获取锁,获取成功后返回true。从这个方法可以看出state变量是实现可重入性的关键。   
 static final class FairSync extends Sync {
        private static final long serialVersionUID = -3000897897090466540L;

        final void lock() {
            //调用的是AQS里面的acquire方法 ,具体AQS这个方法的源码分析见博文末尾的连接
            acquire(1);
        }

        //在AQS中这个方法是一个空实现
        protected final boolean tryAcquire(int acquires) {
            // 当前线程
            final Thread current = Thread.currentThread();
           // 获取state状态,0表示未锁定,大于1表示重入
            int c = getState();
            if (c == 0) {
                 // hasQueuedPredecessors查看队列中是否有比它等待时间更久的线程
                //公平锁比非公平锁多了这行代码
                if (!hasQueuedPredecessors() &&
                    compareAndSetState(0, acquires)) {
            // 没有比当前线程等待更久的线程了,通过CAS的方式修改state
            // 成功之后,设置当前拥有独占访问权的线程
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            else if (current == getExclusiveOwnerThread()) {
                // 独占访问权的线程就是当前线程,重入
    	        // 此处就是【可重入性】的实现
                int nextc = c + acquires;
                if (nextc < 0)
                    throw new Error("Maximum lock count exceeded");
             // 直接修改state
                setState(nextc);
                return true;
            }
            return false;
        }
    }

再看下非公平锁源码:

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

         
        final void lock() {
            //公平锁中没有这一个if判断,该方法是尝试将state 的值由0改成1,设置成功则说明
            当前没有其他线程持有该锁,不用再去排队了。否则 则需要acquire方法去排队
            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) {
                //非公平锁跟公平锁就这一行不一样。非公平锁没有hasQueuedPredecessors()方法
              //不需要查看队列中是否有比它等待时间更久的线程
                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源码

 ReentrantLock和synchronized使用的场景是什么,实现机制有什么不同

 ReentrantLock和synchronized都是独占锁

synchronized:
    1、是悲观锁会引起其他线程阻塞,java内置关键字,
    2、无法判断是否获取锁的状态,锁可重入、不可中断、只能是非公平
    3、加锁解锁的过程是隐式的,用户不用手动操作,优点是操作简单但显得不够灵活
    4、一般并发场景使用足够、可以放在被递归执行的方法上,且不用担心线程最后能否正确释放锁

   5、可以修饰方法、修饰代码块

ReentrantLock:
    1、是个Lock接口的实现类,是悲观锁,
    2、可以判断是否获取到锁,可重入、可中断、可公平可不公平
    3、需要手动加锁和解锁,且 解锁的操作尽量要放在finally代码块中,保证线程正确释放锁
    4、在复杂的并发场景中使用在重入时要却确保重复获取锁的次数必须和重复释放锁的次数一样,否则可能导致       其他线程无法获得该锁。
    5、创建的时候通过传进参数true创建公平锁,如果传入的是false或没传参数则创建的是非公平锁
    6、底层不同是AQS的state和FIFO队列来控制加锁

    7、只能修饰代码块

并发编程核心底层AQS你知道多少_这是王姑娘的微博的博客-CSDN博客一、你知道AQS吗?核心思想是什么?干嘛用的?二、源码看下重要的几个方法以及流程步骤三、​你知道的AQS有几种同步方式,实现同步器一般要覆盖哪些方法面试官:AQS你知道的吧,简单介绍一下面试的时候经常会被面试官这么猛的一问,支支吾吾半天没有很清晰的思路来做一个很好的回答,本博文重点针对AQS是什么,干什么用的,核心思想是什么,加锁/解锁的步骤是哪些、实现同步器的话哪些核心方法是必不可少的进行了描述,要耐心看完呀~一、你知道AQS吗?核心思想是什么?干嘛用的?AQS...https://blog.csdn.net/wnn654321/article/details/123584648 

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值