• 并发编程:不得不说的ReentrantLock


    从Lock讲起

    Lock:一个接口,定义了在jdk层面上灵活实现锁的一种方式。
    实现该接口的类是ReentrantLock。ReentrantLock这个单词的翻译是重入锁。

    重入锁

    持有锁的线程可以再次获取锁,增加重入次数,释放的锁时候也要将次数减为0。synchronized和ReentrantLock都是重入锁。

    ReentrantReadWriteLock:重入读写锁,读和读可以共享,读和写、写和写存在互斥关系。

    ReentrantLock的原理

    加锁流程

    一开始线程A调用lock方法抢占锁会通过CAS更新锁的状态State字段(0,1),假如更新成功则认为获取到了锁 --->
    此时某个线程(可能是A自身)又调用了lock方法,此时会先去查看state:
    假如等于0则直接去CAS抢锁;
    不等于0,且当前线程已经是占有锁的线程,重入一次state+1;
    不等于0,当前线程也没有持有锁---->
    把当前节点封装成EXCLUSIVE NODE,初次加入链表时 自旋加入链表(先初始化一个空的头结点,并设置head和tail都指向该节点,初始化成功或者没初成功的线程 都去自旋更新尾节点);
    封装到链表以后,假如prev是头结点,则再次尝试获取锁;
    获取失败后检查前置节点的状态,如果不是SIGNAL,则自旋改为SIGNAL (-1),假如是SIGNAL,就可以挂起线程了。

    解锁流程

    CAS将state - 1,假如state减到0了(因为重入可能需要释放多次),表示真正地释放锁,并唤醒后一个节点。唤醒时会将当前节点status从SIGNAL改成0,此时后面的线程假如抢到了锁,会执行之前自旋获得锁方法后续部分---> 将头结点指向自己,将旧的头结点释放(old.next = null),并将当前节点变成空节点。此时就完成借节点的释放。

    interrupt

    注意在加锁和解锁的过程中,其他线程调用了被挂起线程的interrupt方法,被挂起的线程是无法立即响应的,该线程被唤醒后会查看interrupt状态,假如被interrupt过,就在之后调用线程的interrupt方法。

    ReentrantLock和synchronize的区别

    • ReentrantLock是JUC包提供给我们的锁,synchronize是jvm级别的关键字
    • 释放:synchronize同步块执行结束或者抛异常;ReentrantLock调用unlock()方法
    • ReentrantLock可以灵活地控制锁,提供了公平锁
    • ReentrantLock有tryLock方法在阻塞时可以中断
    • ReentrantLock条件等待更灵活
  • 相关阅读:
    ↗☻【高性能网站建设进阶指南 #BOOK#】第3章 拆分初始化负载
    ↗☻【高性能网站建设进阶指南 #BOOK#】第7章 编写高效的JavaScript
    【JavaScript】text
    ↗☻【高性能网站建设进阶指南 #BOOK#】第5章 整合异步脚本
    ↗☻【高性能网站建设进阶指南 #BOOK#】第10章 图像优化
    利用十大最佳游戏开发工具开发游戏
    传奇服务器端/客户端 完整源代码
    order by union 应用实例 mssql
    Nine Digits Expression
    Ninedigit Fractions
  • 原文地址:https://www.cnblogs.com/fcb-it/p/13288572.html
Copyright © 2020-2023  润新知