jdk 源码系列之 ReentrantLock
最近将 ReentrantLock 学了一遍同时也把源码读了一遍,记录下学习的过程
JDK 源码系列
使用
使用锁机制,来保障线程安全
或者你可以使用 tryLock() 方法,在多线程中,当一个线程释放锁的时候,就尝试去获取锁。
这种方法,在确保获取锁的时候才会解锁,并且在未获锁时不会尝试去解锁。
如果尝试去获取锁的时候太长,也可以给获取锁的这个过程加上时间,超时则直接中断线程。
如果希望当前锁的模块不是立刻执行,也可以调用 await 机制
有时候,当遇到过长的业务流程,导致持有的时间太长了,可以考虑打断锁的机制,释放锁。
应用场景比较
源码
先看类
向上继承了 Lock 接口,以及 Serializable,都是实现了 Lock 的方法。
void lock() 获取锁
void lockInterruptibly() 中断锁机制
boolean tryLock() 其他线程有释放锁,才调用获取锁
boolean tryLock(long time, TimeUnit unit) 给定时间内线程是空闲时间,且其他线程有释放锁,才调用获取锁
void unlock() 释放锁
Condition newCondition() 给锁绑定条件
实例化锁
这里,有两个构造,一个是无参构造,一个是传入布尔值。
这里默认是构造一个非公平锁,也可以直接设置创建什么类型锁,比如公平锁、非公平锁。
我们先看看公平锁。
公平锁,当前的线程会进入一个队列中最后一个,等待到他的上锁。如果当前的线程表示获取到锁,且也是队列的第一个位置。会将自己变成锁的独占线程,只有自己才持有这个锁的对象。同时这里进行了 CAS 的原子交换,设置状态为1。其中 0 为 未上锁状态,1为上锁状态。
非公平锁
非公平锁,先进行抢占锁,先查看线程是否释放了锁,如果释放了,当前线程则直接上锁,这样的好处就是减少了线程之间的等待,加快了上锁的机制,避免了排队时竞争所导致的延时,提高了性能,如果没有释放锁,则进入到队列的最后一个等待上锁。
小结一下。
释放锁机制。
锁的释放,比较简单。将锁状态重新设置回 0,同时独占线程也设置null,之后唤醒后面的队列里面的线程,完成释放。
源码总结
ReentrantLock 创建的时候,默认是非公平锁,不过你也可以在构造的时候,也可以创建一个公平锁。 其中通过 CAS 改变 state 的状态来改变锁的数值, 0 表示有锁可以获取,1 表示锁已被获取,来设置锁的独占线程。
在公平锁的机制中,请求锁的线程会直接排到一个队列中(通过一个双向链表来模拟的队列)的最后一个,去获取锁。
非公平锁的机制中,请求锁的线程首先会先通过 CAS 来改变 state 的锁状态,如果可以改变(0 -> 1),则直接获取到锁,将自身设置成独占锁。这样的好处就减少了一些进队列、加载队列、唤醒线程等性能消耗。如果未能修改到 state 的状态,也会变成公平锁的机制,进入到队列的最后一个,等待到它去获取锁。
锁的释放,将 state 重新设置回 0,同时独占线程(你也可以认为这是持有锁的线程对象)设置null,之后唤醒排在它下个的线程。这一系列步骤做完,则宣告锁的释放。
优缺点
非公平锁,确实性能比较高。不过也有一个显而易见的缺点,我们可以想象一下,当你在排队吃饭的时候,轮到你吃饭的时候,这时候突然来一个人插在你前面,提前打饭了,导致你打饭时间变长了,如果这时候在有几个人在也突然插到你前打饭,又会继续导致你打饭时间变得更长。那如果放到线程里面,突然其他线程提前获取到了锁,那会导致当前线程获取到锁时间变长,而导致线程阻塞,迟迟未获取到锁。
所以得根据业务去选择合适的锁类型,进行上锁,尽可能的避免有一些重要的业务因为上锁而阻塞到。
声明
作者: Sinsy 本文链接:https://blog.sincehub.cn/2020/11/10/jdk-reentrantLock/
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文声明。 如您有任何商业合作或者授权方面的协商,请给我留言:550569627@qq.com
引用
版权声明: 本文为 InfoQ 作者【sinsy】的原创文章。
原文链接:【http://xie.infoq.cn/article/3758d499e885706b7aa83b097】。文章转载请联系作者。
评论 (3 条评论)