【Java八股-第六期】Lock锁 - Java基础

提纲:

🔥基础八股知识

  • 原理

    • 获取及释放

    • AQS 结构

  • ReentrantLock

    • tryAcquire

    • acquireQueue

    • tryRelease

    • 与 Sychronized 比较

  • ReentrantReadWriteLock

    • 写锁 tryAcquire

    • 读锁 tryAcquire

    • tryAcquireShared

  • Semaphore

    • 信号量锁

🎈面试八股真题

  • 1、Java中synchronized 和 ReentrantLock 有什么不同

  • 2、什么是线程安全

  • 3、说一说自己对于 synchronized 关键字的了解

  • 4、说说自己是怎么使用 synchronized 关键字

  • 5、锁的优化机制了解吗

  • 6、产生死锁的四个必要条件

  • 7、如何避免死锁

一、Lock

1. 原理

  • Lock 锁都是使用 AQS 同步器来实现锁,通过重写 tryAcquire 方法获取锁,通过 tryRelease 方法释放锁

  • AQS 结构

    • 1、ExclusiveOwner:表示当前持有锁的线程

    • 2、state:一个 volatile int 整形,用来表示锁的添加情况,不同的锁有不同的实现,例如 ReentrantReadWriteLock 通过将 32 的 int 分为高 16 位与低 16 位分别表示读写锁的使用情况

    • 3、AcquireQueue:等待队列,是一个双向的链表,当一个被阻塞的线程加入时,会将其前驱节点标记为 -1,表示在锁释放时,需要唤醒后继节点,利用双向链表的性质可以实现公平锁,ReentrantReadWriteLock 通过对链表添加 share/exclusive 属性来实现读写锁特性

    • 4、Condition:可以通过 AQS 获取多个 Condition 对象,每一个 Condition 对象本质上就是一个等待队列,线程调用哪一个 Condition 的 await 方法,就将线程放入哪一个等待队列,同样的在唤醒时,调用哪个 Condition 的 signal 方法,就唤醒对应的线程

2.ReentrantLock

  • tryAcquire

    • 1、当一个线程尝试获取锁,会先判断 state 是否为 0,若为 0,进行加锁,若不为 0,判断 ExclusiveOwner 是否是自己,若是自己,则发生锁重入,加锁

    • 2、加锁时,使用 CAS 的方式为 state 变量加 1,若成功,则成功获取锁/锁重入,若失败,则获取锁失败

    • 3、线程获取锁失败,会进入 acquireQueue 方法

  • acquireQueue

    • 1、在 acquireQueue 方法中,线程会在一个死循环中不断尝试获取锁,只有在成功获取锁后才会退出循环

    • 2、在循环中,获取锁失败后,会使用 shouldParkAfterFail 方法,调用 unsafe 的park 方法阻塞线程

    • 3、若在 park 的过程中被 interrupt 打断,若调用的是 lock 获取锁,是不可打断锁逻辑,线程在被打断后会重新回到 while 循环中,继续尝试获取锁,并且会将打断标记重置,保证线程可以继续被 park;若调用的是 lockInterruptbly,则是可打断逻辑,调用的 acuireInterruptibly等待队列方法,在被打断后,通过直接抛出 InterruptException 异常的方式来终止等待

  • tryRelease

    • 1、因为支持可重入锁,必须释放到 state 等于 0 才算完全释放

    • 2、释放时,会将 ExclusiveOwner 重置为 null,并唤醒 EntryList 等待队列,EntryList 等待队列中所有的标记为 -1 的节点被唤醒后都会去唤醒其后继节点,被唤醒的节点会继续进入 acquireQueue方法获取锁

    • 3、若实现公平锁,则线程在被唤醒时,会检查其是否存在前驱节点,若存在则继续阻塞与 Sychronized 比较:可打断/可重入/可公平/多 Condition 等待条件

3.ReentrantReadWriteLock

  • 写锁 tryAcquire

    • 1、检查 state 是否为 0,若为 0,添加写锁

    • 2、若不为 0,检查写锁部分是否为 0,若写锁部分为 0,表示当前有线程占用读锁,获取锁失败

      • # 因此在线程持有读锁的情况下不能获取写锁,必须先释放读锁再获取写锁,否则相当于产生了一个死锁条件

    • 3、若写锁部分不为 0,表示当前正被添加写锁,若 ExclusiveOwner 指向自己,则是写锁重入,添加写锁

    • 4、同样使用 CAS 的方式添加锁,若失败则获取锁失败

    • 5、获取锁失败执行 acuireQueue 方法,原理与 ReentrantLock 原理一致

  • 读锁 tryAcquire

    • 1、仅判断 state 写锁部分是否为 0,只要为 0 就尝试获取锁

    • 2、获取锁失败进入等待队列 tryAcquireShared 方法

  • tryAcquireShared

    • 1、获取写锁的线程被放入等待队列时,会标记节点为 Exclusive,获取读锁的线程被放入等待队列时,会标记为 Shared

    • 2、在 tryAcquireShared 等待队列方法中,不断重试获取读锁,当获取到读锁时,会唤醒等待队列中所有的 Shared 节点

4.Semaphore

  • 信号量锁,state 初始值为可允许的最大并发访问资源线程数,每一次线程获取锁都为 state - 1,当 state = 0 时阻塞获取锁的线程

二、面试八股真题🎈🎈🎈

1、Java中synchronized 和 ReentrantLock 有什么不同

  • 相似点:

    • 这两种同步方式有很多相似之处,它们都是加锁方式同步,而且都是阻塞式的同步,也就是说当如果一个线程获得了对象锁,进入了同步块,其他访问该同步块的线程都必须阻塞在同步块外面等待,而进行线程阻塞和唤醒的代价是比

剩余60%内容,订阅专栏后可继续查看/也可单篇购买

代码鹿のJAVA八股面试题总结 文章被收录于专栏

【📫专栏目录在最底部📫】 - 本专栏适合于JAVA已经入门的学生或人士,有一定的编程基础。 - 本专栏特点: 本专刊囊括了JAVA、Spring、计算机网路、操作系统、计算机网络、MySQL、算法与数据结构、中间件等一系列知识点,总结出了高频面试考点(附有答案),事半功倍,为大家春秋招助力。 - 本专栏内容分为五章

全部评论

相关推荐

问的很简单都秒了,但是面试官没开摄像头,疑似kpi,无后续。一面(35min)(25/3/6)(无后续)    1、自我介绍    2、介绍一下你的那个Python相关项目(本科毕设,web系统+算法模型提供部分接口)    3、Java面向对象有哪些特点呢?详细说一下。    4、介绍一下hashmap;为什么要把链表转换为红黑树呢?红黑树查找的时间复杂度?1.7和1.8的区别。    5、介绍一下concurrentHashmap。    6、synchronized锁和Lock锁有什么区别?    7、公平锁的一个底层是怎么实现的呢?    8、线程池的核心参数、拒绝策略、提交一个任务执行流程?    9、spring有哪些特点?(ioc/aop)    10、spring中对于循环依赖是怎么解决的?    11、MySQL和redis的区别?    12、MySQL的索引结构是什么?    13、MySQL的事务有哪些特性?怎么保证?    14、MySQL的默认隔离级别?可重复读是怎么做到的呢?    15、介绍一下MVCC和快照读readview。    16、一般在什么场景下会使用redis?    17、对于大量的请求,如果此时缓存中还没有写入数据怎么办?    18、介绍一下redis实现的分布式锁。    19、有用过es和mongo DB吗?(知道,没用过)    20、消息中间件用过吗?说一下你的使用场景?    21、一个场景,如果说有一个接口响应的比较慢,如果说让你排查,你会怎么去排查?(上下游接口、大key问题,只答了两,后面试官补充)    无手撕,反问业务。
胖墩墩的查理在学c语言:哥们我是五号面的 流程差不多
查看21道真题和解析
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客企业服务