首页 > 其他分享 >ReentrantLock的介绍

ReentrantLock的介绍

时间:2023-07-10 18:33:20浏览次数:33  
标签:Node AQS lock ReentrantLock 介绍 state 线程 公平

Java中提供了两种锁:synchronized和lock锁,ReentrantLock属于lock锁,是互斥锁,可以让多线程执行期间只有一个线程在执行指定一段代码。

二、ReentrantLock的lock方法:

2.1简单分析

进入lock方法后,发现内部调用了sync.lock()方法,去找方法的实现,发现了两个实现。

Fairsync 公平锁,每个线程都会在执行lock方法时,查看是否有线程排队,如果有,直接去排队,如果没有才会去尝试竞争一个锁资源。

NonfairSync 非公平锁,每个线程都会在执行lock方法时,先尝试获取所资源,获取不到再排队。

非公平锁的效率更高,在new Reentrantlock时候传入参数true。

公平锁直接使用无参构造方法。

从源码的角度也发现了公平锁直接调用acquire方法尝试获取锁,而非公平锁会先基于CAS方式尝试获取锁资源,如果获取不到才会执行acquire方法。

2.2分析AQS

AQS就是AbstractQueuedSynchronizer类,AQS内部维护着一个队列,有三个核心属性state head tail

 

2.3lock()方法源码

 1 //非公平锁的lock方法
 2 final void lock() {
 3 //    使用CAS方式,将state从0改为1
 4     if (compareAndSetState(0, 1))
 5 //        证明state修改成功,也就代表获取锁资源成功
 6 //        将当前线程设置到AQS的exclusiveOwnerThread(AOS中),代表当前线程拿着锁资源
 7         setExclusiveOwnerThread(Thread.currentThread());
 8     else
 9         acquire(1);
10 }
11 //公平锁的lock方法
12 final void lock() {
13     acquire(1); }

 

 三、acquire()方法

//不管是公平锁还是非公平锁,都会调用acquire方法
public final void acquire(int arg) {
//    tryAcquire方法分为两种实现,第一种是公平锁,第二种是非公平锁
//    公平锁操作:如果state为0,再看是否有线程排队,如果有就去排队.如果state不为0,看是否是自己拿着锁资源,是否可以做一个锁重入的操作,如果可以直接获取锁
//    非公平锁:如果state为0,直接尝试CAS修改.如果state不为0,如果是锁重入的操作,直接获取锁
    if (!tryAcquire(arg) &&
//            addWaiter在线程没有通过tryAcquire拿到锁资源时,需要将当前线程封装为Node对象,去AQS排队
//            acquireQueued方法查看当前线程是否排在队伍前面的,如果是尝试获取锁资源,如果长时间没拿到锁,也需要将当前线程挂起
            acquireQueued(addWaiter(AbstractQueuedSynchronizer.Node.EXCLUSIVE), arg))
        selfInterrupt();
}

 

 

 四、tryAcquire()方法

是AQS提供的,内部并没有任何的实现,需要继承AQS的类自己去实现逻辑代码,查看到tryAcquire在ReentantLock中提供了两种实现:公平锁和非公平锁

非公平锁

//非公平锁的实现方式
final boolean nonfairTryAcquire(int acquires) {
//    获取当前线程
    final Thread current = Thread.currentThread();
//    获取AQS的state
    int c = getState();
//    如果state为0,代表当前没有线程占用锁资源
    if (c == 0) {
//        直接基于CAS的方式,尝试修改state,从0修改为1,如果成功就代表拿到锁资源
        if (compareAndSetState(0, acquires)) {
//            将exclusiveOwnerThread属性设置为当前线程
            setExclusiveOwnerThread(current);
//            返回true
            return true;
        }
    }
//    说明state肯定不为0,不为0就代表当前lock被线程占用
//    判断占用锁资源的线程是不是当前线程
    else if (current == getExclusiveOwnerThread()) {
//        锁重入操作,直接对state加1
        int nextc = c + acquires;
//        判断锁重入是否达到最大值
        if (nextc < 0) // overflow
            throw new Error("Maximum lock count exceeded");
//        设置AQS中的state
        setState(nextc);
        return true;
    }
    
    return false;
}

 

 公平锁

//公平锁实现
protected final boolean tryAcquire(int acquires) {
//    获取当前线程
    final Thread current = Thread.currentThread();
    int c = getState();
    if (c == 0) {
//        没有线程占用锁资源,首先查看有没有线程排队
        if (!hasQueuedPredecessors() &&
//                如果没有线程排队,尝试CAS获取锁资源
                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;
}
}

 

 公平锁和非公平锁的tryAcquire方法唯一区别就是,当判断state为0之后

  • 公平锁会 先查看是否有线程在排队,如果有,直接返回false,如果没有线程排队,执行CAS尝试获取锁资源
  • 非公平锁不管有没有线程排队,直接以CAS的方式尝试获取锁资源

五、addWaiter()方法

在线程执行tryAcquire方法没有获取锁资源之后,会返回false,再配置上if中的!操作,会执行&&后面的方法,而在

acquireQueued(addWaiter(Node.EXCLUSIVE), arg))的参数中执行了addWaiter()方法,要将当前获取锁失败的线程封装为Node,排队到AQS的队列中。
整体逻辑为先初始化Node节点,将当前线程传入,并且标识为互斥锁。
尝试将当前Node插入到AQS队列的末尾,
  • 队列为空,执行enq,先初始化空Node作为头,再将当前Node插入,作为tail
  • 队列不为空,直接将当前Node插入,作为tail
//获取锁失败 排队到AQS的队列中
private Node addWaiter(Node mode) {
//    将线程封装为Node对象
    Node node = new Node(Thread.currentThread(), mode);
    // 获取到tail节点
    Node pred = tail;
//    如果tail节点不为null,表示正在有人排队
    if (pred != null) {
//        将当前节点的prev指向tail
        node.prev = pred;
//        避免并发问题,以CAS方式将tail指向当前线程
        if (compareAndSetTail(pred, node)) {
//            将之前的tail的next指向当前节点
            pred.next = node;
//            返回当前节点
            return node;
        }
    }
//    如果在队列为空或者CAS操作失败后,执行enq方法将当前线程排队队列末尾
    enq(node);
    return node;
}

 

六、ReentantLock的acquireQueued方法

首先查看当前node是否排在队列的第一个位置(不算head),直接再次执行tryAcquire方法竞争锁资源,尝试将当前线程挂起,最终排在有效节点后才会将当前线程挂起。

七、ReentantLock的unlock方法

unlock释放锁操作不分为公平和非公平,都是执行sync的release方法

释放锁的核心就是将state从大于0的数值改成0即为释放锁成功,unlock应该会涉及到AQS队列中阻塞的线程进行唤醒,阻塞的方法是park方法,唤醒必然是unpark方法。

 

 

 

 

标签:Node,AQS,lock,ReentrantLock,介绍,state,线程,公平
From: https://www.cnblogs.com/zhang12345/p/17541976.html

相关文章

  • Unity UGUI的Mask(遮罩)组件的介绍及使用
    UnityUGUI的Mask(遮罩)组件的介绍及使用1.什么是Mask组件?Mask(遮罩)组件是UnityUGUI中的一个重要组件,用于限制子对象的可见区域。通过设置遮罩组件,可以实现一些特殊效果,如显示部分图片、裁剪文本等。2.Mask组件的工作原理Mask组件通过将子对象与遮罩对象进行比较,只显示与遮罩......
  • 系统介绍【项目】
    介绍产品时面试官会考察应聘者的沟通能力和思考能力,我们大部分情况都是做产品的一个功能或一个模块,但是即使是这样,自己有没有把整个系统架构或产品搞清楚,并能介绍清楚,为什么做这个系统?这个系统的价值是什么?这个系统有哪些功能?优缺点有哪些?如果让你重新设计这个系统你会如何设计?......
  • Docker学习路线1:介绍
    Docker是什么?Docker是一个开源平台,通过将应用程序隔离到轻量级、可移植的容器中,自动化应用程序的部署、扩展和管理。容器是独立的可执行单元,封装了运行应用程序所需的所有必要依赖项、库和配置文件,可以在各种环境中稳定地运行。什么是容器?容器是一种轻量级、可移植和隔离的软件......
  • IDEA介绍
    集成开发环境(IDE)简介集成开发环境(IDE,IntegratedDevelopmentEnvironment)是为程序开发提供便利的应用程序。通常包括代码编辑器、编译器、调试器和图形用户界面等工具。它们集成了代码编写、分析、编译、调试等功能,提供一体化的开发软件服务套件。任何具备这些特性的软件或软件套......
  • MD5加密介绍
      MD5的全称是Message-DigestAlgorithm5,Message-Digest泛指字节串(Message)的Hash变换,就是把一个任意长度的字节串变换成一定长的大整数。MD5将任意长度的"字节串"变换成一个128bit的大整数,并且它是一个不可逆的字符串变换算法,换句话说就是,即使你看到源程序和算法描述,也无法......
  • jmeter--后置处理器之json提取器介绍和使用
    1、json提取器使用场景jmeter后置处理器中,有三种提取器组件:json提取器、xpath提取器、正则表达式提取器,这三种组件的功能相同,只是分别使用三种不同的方式进行提取数值。一般在使用jmeter来调接口时,B接口的请求参数值是A接口的响应值时,需要通过提取器组件来提取出A接口相应中的......
  • Redis缓存同步1-策略介绍
    缓存数据同步策略示意图在大多数情况下,我们通过浏览器查询到的数据都是缓存数据,如果缓存数据与数据库的数据存在较大差异的话,可能会产生比较严重的后果的。所以,我们应该也必须保证数据库数据、缓存数据的一致性,这就是缓存与数据库的同步。缓存数据同步策略缓存数据同步,常见的......
  • Unity UGUI的Slider(滑动条)件组的介绍及使用
    UnityUGUI的Slider(滑动条)件组的介绍及使用1.什么是Slider组件?Slider(滑动条)是UnityUGUI中的一种常用UI组件用,于在用户界面中实现滑动选择的功能。通过拖动滑块,用户可以选择一个数值范围的内值。2.Slider组件的工作原理Slider组件由两部分组成:滑动区域和滑块。滑动区域用于显......
  • Unity UGUI的Toggle(复选框)组件的介绍及使用
    UnityUGUI的Toggle(复选框)组件的介绍及使用1.什么是Toggle组件?Toggle(复选框)是UnityUGUI中的一个常用组件,用于实现复选框的功能。它可以被选中或取消选中,并且可以代码通过其制控状态。2.Toggle组件的工作原理组Toggle件由两个部分组成:背景记标和。景背用于显示复选框的外观表......
  • Unity UGUI的Image(图片)组件的介绍及使用
    UGUI的Image(图片)组件的介绍及使用1.什么是UGUI的Image(图片)组件?UGUI的Image(图片)组件是Unity引擎中的一种UI组件,用于显示2D图像。它提供了一种简单而灵活的方式来在游戏中加载和显示图片。2.为什么要使用UGUI的Image(图片)组件?使用UGUI的Image组件可以方便地在游戏中展示各种图片......