相信大家在工作或者面试过程中经常听到重入锁这个概念, 或者与关键字 synchrozied 的对比, 栈长面试了这么多人, 80% 的面试者都没有答对或没有答到点上, 或者把双重效验锁搞混了, 哭笑不得..
那么你对重入锁了解有多少呢? 今天, 栈长帮大家撕开重入锁的面纱, 来见识下重入锁的真实容颜..
什么是重入锁
java.util.concurrent.locks.ReentrantLock
这个是 JDK @since 1.5 添加的一种颗粒度更小的锁, 它完全可以替代 synchronized 关键字来实现它的所有功能, 而且 ReentrantLock 锁的灵活度要远远大于 synchronized 关键字.
从类结构图看出, ReentrantLock 实现了 Lock 接口, ReentrantLock 只是 Lock 接口的一个实现而已.
java.util.concurrent.locks.Lock
它们都是 java.util.concurrent 包里面的内容 (俗称 JUC, 并发包), 也都是 JDK 1.5 开始加入的.
为什么叫重入锁呢?
ReentrantLock, 我们把它拆开来看就明了了.
Re-Entrant-Lock: 即表示可重新反复进入的锁, 但仅限于当前线程;
- public void m() {
- lock.lock();
- lock.lock();
- try {
- // ... method body
- } finally {
- lock.unlock()
- lock.unlock()
- }
- }
如示例代码所示, 当前线程可以反复加锁, 但也需要释放同样加锁次数的锁, 即重入了多少次, 就要释放多少次, 不然也会导入锁不被释放.
试想一下, 如果不设计成可重入锁, 那自己如果反复给自己加锁, 不是会把自己加死锁了吗? 所以, 到现在, 重入锁的概念大概应该清楚了吧?
重入锁最重要的几个方法
这几个方法都是 Lock 接口中定义的:
1)lock()
获取锁, 有以下三种情况:
锁空闲: 直接获取锁并返回, 同时设置锁持有者数量为: 1;
当前线程持有锁: 直接获取锁并返回, 同时锁持有者数量递增 1;
其他线程持有锁: 当前线程会休眠等待, 直至获取锁为止;
2)lockInterruptibly()
获取锁, 逻辑和 lock() 方法一样, 但这个方法在获取锁过程中能响应中断.
3)tryLock()
从关键字字面理解, 这是在尝试获取锁, 获取成功返回: true, 获取失败返回: false, 这个方法不会等待, 有以下三种情况:
锁空闲: 直接获取锁并返回: true, 同时设置锁持有者数量为: 1;
当前线程持有锁: 直接获取锁并返回: true, 同时锁持有者数量递增 1;
其他线程持有锁: 获取锁失败, 返回: false;
4)tryLock(long timeout, TimeUnit unit)
逻辑和 tryLock() 差不多, 只是这个方法是带时间的.
5)unlock()
释放锁, 每次锁持有者数量递减 1, 直到 0 为止. 所以, 现在知道为什么 lock 多少次, 就要对应 unlock 多少次了吧.
6)newCondition
返回一个这个锁的 Condition 实例, 可以实现 synchronized 关键字类似 wait/ notify 实现多线程通信的功能, 不过这个比 wait/ notify 要更灵活, 更强大!
重入锁大概的用法
- class X {
- private final ReentrantLock lock = new ReentrantLock();
- // ...
- public void m() {
- lock.lock(); // block until condition holds
- try {
- // ... method body
- } finally {
- lock.unlock()
- }
- }
- }}
看见没有, 加锁和释放锁都在方法里面进行, 可以自由控制, 比 synchronized 更灵活, 更方便. 但要注意的是, 释放锁操作必须在 finally 里面, 不然如果出现异常导致锁不能被正常释放, 进而会卡死后续所有访问该锁的线程.
synchronized 是重入锁吗?
那么问题来了, synchronized 是重入锁吗?
你可能会说不是, 因为 ReentrantLock 既然是重入锁, 根据推理, 相反, 那 synchronized 肯定就不是重入锁, 那你就错了.
答案是: yes, 为什么? 看下面的例子:
- public synchronized void operation(){
- add();
- }
- public synchronized void add(){
- }
operation 方法调用了 add 方法, 两个方法都是用 synchronized 修饰的, add() 方法可以成功获取当前线程 operation() 方法已经获取到的锁, 说明 synchronized 就是可重入锁.
面试常问的 Synchronized 的几种用法推荐看下这篇文章: Synchronized 有几种用法? https://mp.weixin.qq.com/s/9h6VjHAmLA3twD6Y-FqfwA .
总结
今天, 重入锁就大概写到这里了, 其实重入锁就是一种颗粒度更小的锁, 控制更方便, 更强大, 栈长只是简单介绍一下重入锁的基本概念及用法, 但远不止这么简单, 还有很多, 一篇也难也详尽, 够写好多篇了.
来源: http://www.bubuko.com/infodetail-3105234.html