reentrant 英[riːˈɛntrənt] 美[ˌriˈɛntrənt] 先学会读。单词原意是可重入的

  1. 考察显示锁的使用。可延伸知识点
    1. 独占锁 & 共享锁
      1. 独占锁 - 悲观锁(不能同时被多个线程持有 - synchronized锁 & ReentrantLock)
      2. 共享锁 - 乐观锁(ReentrantReadLock )
      3. 读共享、写排他
    2. 重入锁  
      1. 方法进行深层次调用时,获取同一把锁能够获取到,不会死锁  
    3. 使用范式
      1. finally

                Lock lock = new ReentrantLock();
                lock.lock();
                try{
                    //一顿操作
                }finally {
                    lock.unlock();
                }    
    4. CAS原理实现乐观锁
      1. 原理 : 在线程对数据进行修改时,需要对比现在持有的变量和原始地址中的值是否相同,若相同则替换成功,若不同替换失败
      2. 实现乐观锁 = 自旋 + CAS
      3. 问题 :
        1. ABA问题 - jdk AtomicStampedReference  AtomicMarkableReference
        2. 自旋时间过长会导致CPU消耗过大
        3. 一次操作只能修改一个内存地址的变量 AtomicReference<V>
      4. jdk相关实现类
        1. AtomicInteger
        2. AtomicReference<V>  多个共享变量一起操作
        3. AtomicReferenceArray  操作时是复制了原数组一份,修改后原数组的值不变
        4. AtomicMarkableReference 解决ABA问题,但只关心是否改变
        5. AtomicStampedReference 解决ABA问题,会记录改变次数.可通过getStamp()获取
    5. condition - wait&notify --> ***
    6. CHL队列锁 - 基于链表
      1. 每个线程拿锁时创建一个Node,locked状态置为true,把自己放到链表的tail,然后把myPred指向之前的tail,之后向前循环check locked状态,直到为false时自己就拿到了锁
    7. AQS - 抽象队列同步器
      1. CHL实现的变种 -- 双向链表
      2. 实现多线程访问共享资源的同步框架FIFO
      3. 采用模板方法,一些方法需要继承者实现,但为什么不设计成abstract方法(抽象方法都要实现,为开发者考虑,独占式获取锁只实现独占方法,共享方式只实现共享方法)
        1. 独占式 tryAcquire、 tryRelease、isHeldExclusively
        2. 共享 tryAcquireSharedtryReleaseSharedisHeldExclusively
        3. 方法都有一个参数 ?
      4. state属性
        1. volatile int state 代表共享资源
        2. 提供三种访问方式 getState()、setState()、compareAndSetState() 
          1. setState 和 compareAndSetState 有什么区别 ? 前者不是有安全问题吗,为什么还存在。(setState 是在已经拿到锁的情况下调用,不会有安全问题)
        3. ReentrantLock 用来标记拿锁的次数、CountDownLatch 用来标记任务的个数
      5. acquire()方法
        1. tryAcquire()
        2. addWaiter() 默认独占式 - 自旋添加节点
        3. acquireQueued() 真正的拿锁方法 返回等待过程中是否被中断过,自旋+阻塞获取资源 
          1. 若前驱结点是head且拿到了锁的情况下,把当前节点置为head节点,并把原head节点脱离
          2. shouldParkAfterFailedAcquire(Node, Node) 返回前驱节点是否处于等待状态Node.SIGNAL。并将自己放在此节点的后置节点
          3. parkAndCheckInterrupt() 返回是否被中断过。阻塞当前线程,如果线程被唤醒,检查是被打断还是被正常唤醒
        4. 先去尝试拿锁,拿不到就将自己放在等待队列尾部,然后自旋向前寻找,直到head节点拿到锁为止。即使中间被打算,等待过程也不会中断。而是在拿到锁之后再中断自己
      6. release()方法
        1. tryRelease(arg)
        2. 释放锁成功之后调用 unparkSuccessor(head) -> 将head节点状态置为0,用unpark()唤醒等待队列中最后边的那个未放弃线程
        3. 唤醒acquireQueued方法中的判断,让线程拿到锁
      7. acquireShared()方法
        1. 类似acquire()  
        2. 多一个setHeadAndPropagate()方法。在拿到资源的时候向后唤醒 - 体现共享
        3. 调用了doReleaseShared()
      8. releaseShared()
        1.  doReleaseShared()释放掉资源后,唤醒后继  实际调用unparkSuccessor(head)
    8. 公平锁&非公平锁
      1. 遵循拿锁的顺序
    9. 隐式锁synchronized 对比
      1. lock提供一些除lock()操作之外功能,更加灵活
      2. 非特殊情况下使用synchronized,jdk对它的优化比较大
    10. 模板方法
  2. 实现
    1. ReentrantLock自身并未继承AQS,而是采用内部类Sync继承。屏蔽内部实现、外部调用者不用关心具体细节
    2. 如何实现可重入
      1. tryAcquire if(state ==0 )的else中进行当前线程锁的累加
    3. 公平锁和非公平锁有什么区别
      1. 非公平锁再获取锁的时候不按排队顺序而是随机拿锁 
      2. tryAcquire方法中!hasQueuedPredecessors() 来判定队列中是否有前驱节点在等待锁
    4. 在阻塞之前,线程都会通过shouldParkAfterFailedAcquire去修改其前驱节点的waitStatus=-1。这是为什么?为了release时unparkSuccessor(head) 唤醒后续节点
    5. unparkSuccessor时为什么会出现s==null || s.waitStatus>0的情况,这种情况下,为什么要通过prev指针反向查找Successor节点?
      1. s == null 是因为acquireQueued() 在拿到锁之后会将head.next = null .这样链表就断了,所以要从尾部节点向前找
      2. s > 0 是在cancel的时候,节点在head节点的后继节点断开。导致链表断裂。  

总结: 如何回答这个问题。

1. ReentrantLock 通过定义一个内部类来实现Lock接口,公平锁和非公平锁的实现都是基于这个继承自AQS的内部类Sync。不同的点是在拿锁的时候,非公平锁会直接拿锁,而公平锁会判断队列中是否还有线程在等待锁。如果有就会拿锁失败。

2.后面就是回答AQS原理了

参考AQS https://www.cnblogs.com/waterystone/p/4920797.html    

图解 https://www.jianshu.com/p/b6efbdbdc6fa

ReentrantLock 如何实现非公平锁?和公平锁实现有什么区别的更多相关文章

  1. 第五章 ReentrantLock源码解析1--获得非公平锁与公平锁lock()

    最常用的方式: int a = 12; //注意:通常情况下,这个会设置成一个类变量,比如说Segement中的段锁与copyOnWriteArrayList中的全局锁 final Reentrant ...

  2. ReentrantLock获取到非公平锁的源码

    /** * Performs lock. Try immediate barge, backing up to normal * acquire on failure. */ final void l ...

  3. 图解AQS原理之ReentrantLock详解-非公平锁

    概述 并发编程中,ReentrantLock的使用是比较多的,包括之前讲的LinkedBlockingQueue和ArrayBlockQueue的内部都是使用的ReentrantLock,谈到它又不能 ...

  4. 死磕 java同步系列之ReentrantLock源码解析(一)——公平锁、非公平锁

    问题 (1)重入锁是什么? (2)ReentrantLock如何实现重入锁? (3)ReentrantLock为什么默认是非公平模式? (4)ReentrantLock除了可重入还有哪些特性? 简介 ...

  5. ReentrantLock可重入锁、公平锁非公平锁区别与实现原理

    ReentrantLock是lock接口的一个实现类,里面实现了可重入锁和公平锁非公平锁 ReentrantLock公平锁和不公平锁实现原理 公平锁会获取锁时会判断阻塞队列里是否有线程再等待,若有获取 ...

  6. 【试验局】ReentrantLock中非公平锁与公平锁的性能测试

    硬件环境: CPU:AMD Phenom(tm) II X4 955 Processor Memory:8G SSD(128G):/ HDD(1T):/home/ 软件环境: OS:Ubuntu14. ...

  7. 最全Java锁详解:独享锁/共享锁+公平锁/非公平锁+乐观锁/悲观锁

    在Java并发场景中,会涉及到各种各样的锁如公平锁,乐观锁,悲观锁等等,这篇文章介绍各种锁的分类: 公平锁/非公平锁 可重入锁 独享锁/共享锁 乐观锁/悲观锁 分段锁 自旋锁 01.乐观锁 vs 悲观 ...

  8. Java最全锁剖析:独享锁/共享锁+公平锁/非公平锁+乐观锁/悲观锁

    乐观锁 VS 悲观锁 乐观锁与悲观锁是一种广义上的概念,体现了看待线程同步的不同角度,在Java和数据库中都有此概念对应的实际应用. 1.乐观锁 顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会 ...

  9. Java中的锁-悲观锁、乐观锁,公平锁、非公平锁,互斥锁、读写锁

    总览图 如果文中内容有错误,欢迎指出,谢谢. 悲观锁.乐观锁 悲观锁.乐观锁使用场景是针对数据库操作来说的,是一种锁机制. 悲观锁(Pessimistic Lock):顾名思义,就是很悲观,每次去拿数 ...

随机推荐

  1. 生产环境中的kubernetes 优先级与抢占

    kubernetes 中的抢占功能是调度器比较重要的feature,但是真正使用起来还是比较危险,否则很容易把低优先级的pod给无辜kill.为了提高GPU集群的资源利用率,决定勇于尝试一番该feat ...

  2. JS中==运行机制

    1. 判断两边是否有NaN,如果有则一律返回false 2.判断两边是否含有布尔值,如果有的话则将true转化为1,false转化为0. 3.遇到null或者undefined,则不会进行类型转换,它 ...

  3. Solidity语言系列教程

    Solidity 是一门面向合约的.为实现智能合约而创建的高级编程语言.这门语言受到了 C++,Python 和 Javascript 语言的影响,设计的目的是能在 以太坊虚拟机(EVM) 上运行. ...

  4. ThingJS和传统3D开发的区别

    物联网3D可视化开发已经辐射到各行各业,无论车间还是消防,城市还是粮仓,亦或是地铁.科技园,物联网可视化是科技的进步,也是行业的进步.而传统的3D可视化开发实施起来并不那么乐观.如果使用ThingJS ...

  5. 百万年薪python之路 -- MySQL数据库之 MySQL行(记录)的操作(二) -- 多表查询

    MySQL行(记录)的操作(二) -- 多表查询 数据的准备 #建表 create table department( id int, name varchar(20) ); create table ...

  6. Java描述设计模式(16):代理模式

    本文源码:GitHub·点这里 || GitEE·点这里 一.生活场景 1.场景描述 在电商高速发展的今天,快递的数量十分庞大,甚至出现了快递代理行业,简单的说就是快递的主人没有时间收快递,会指定一个 ...

  7. 5.分析snkrs的Android的DeviceID生产规则

    分析Android的DeviceID生产 前面已经把web端的分析了一些,要想实现其实容易也难,容易是规则很容易,难是时间生成控制很难,我之前大概花了一周时间分析和梳理,然后行为测试,之前我已经讲过c ...

  8. Alpha阶段--第七周Scrum Meeting

    任务内容 本次会议为第六周的Scrum Meeting会议 召开时间为周日下午5点,在潮音餐厅召开,召开时间约为30分钟,对已经完成项目的总结和对今后项目设计的展望 队员 任务 张孟宇 “我的”界面代 ...

  9. 【C#多线程】1.Thread类的使用及注意要点

    Thread随便讲讲 因为在C#中,Thread类在我们的新业务上并不常用了(因为创建一个新线程要比直接从线程池拿线程更加耗费资源),并且在.NET4.0后新增了Task类即Async与await关键 ...

  10. Angular工作笔记(2018/8/8)

    需求1:input标签只允许输入数字且不会出现上下选择按钮 开始设置 type="number" 客户不想要出现上下选择按钮 解决方法: 但是这种写法会有一个限制,只能输入整数,无 ...