理解ReentrantLock的公平锁和非公平锁
学习AQS的时候,了解到AQS依赖于内部的FIFO同步队列来完成同步状态的管理,当前线程获取同步状态失败时,同步器会将当前线程以及等待状态等信息构造成一个Node对象并将其加入到同步队列,同时会阻塞当前线程,当同步状态释放时,会把首节点中的线程唤醒,使其再次尝试获取同步状态。
这时,我有了一个疑问,AQS的同步队列是FIFO的,就是先来排队的先走。那怎么实现非公平锁呢?查阅了一些资料,总算知道了。
首先从公平锁开始看起。
ReentrantLock 的公平锁
ReentrantLock 默认采用非公平锁,除非在构造方法中传入参数 true 。
//默认
public ReentrantLock() {
sync = new NonfairSync();
}
//传入true or false
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
公平锁的 lock 方法:
static final class FairSync extends Sync {
final void lock() {
acquire(1);
}
// AbstractQueuedSynchronizer.acquire(int arg)
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
// 1. 和非公平锁相比,这里多了一个判断:是否有线程在等待
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
}
我们可以看到,在注释1的位置,有个!hasQueuedPredecessors()
条件,意思是说当前同步队列没有前驱节点(也就是没有线程在等待)时才会去compareAndSetState(0, acquires)
使用CAS修改同步状态变量。所以就实现了公平锁,根据线程发出请求的顺序获取锁。
非公平锁的lock方法
static final class NonfairSync extends Sync {
final void lock() {
// 2. 和公平锁相比,这里会直接先进行一次CAS,成功就返回了
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
// AbstractQueuedSynchronizer.acquire(int arg)
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}
/**
* Performs non-fair tryLock. tryAcquire is implemented in
* subclasses, but both need nonfair try for trylock method.
*/
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
//3.这里也是直接CAS,没有判断前面是否还有节点。
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
非公平锁的实现在刚进入lock方法时会直接使用一次CAS去尝试获取锁,不成功才会到acquire方法中,如注释2。而在nonfairTryAcquire方法中并没有判断是否有前驱节点在等待,直接CAS尝试获取锁,如注释3。由此实现了非公平锁。
总结
非公平锁和公平锁的两处不同:
非公平锁在调用 lock 后,首先就会调用 CAS 进行一次抢锁,如果这个时候恰巧锁没有被占用,那么直接就获取到锁返回了。
非公平锁在 CAS 失败后,和公平锁一样都会进入到 tryAcquire 方法,在 tryAcquire 方法中,如果发现锁这个时候被释放了(state == 0),非公平锁会直接 CAS 抢锁,但是公平锁会判断等待队列是否有线程处于等待状态,如果有则不去抢锁,乖乖排到后面。
公平锁和非公平锁就这两点区别,如果这两次 CAS 都不成功,那么后面非公平锁和公平锁是一样的,都要进入到阻塞队列等待唤醒。
相对来说,非公平锁会有更好的性能,因为它的吞吐量比较大。当然,非公平锁让获取锁的时间变得更加不确定,可能会导致在阻塞队列中的线程长期处于饥饿状态。
作者:小北觅
链接:https://www.jianshu.com/p/2ada27eee90b
來源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。
理解ReentrantLock的公平锁和非公平锁的更多相关文章
- java多线程20 : ReentrantLock中的方法 ,公平锁和非公平锁
公平锁与非公平锁 ReentrantLock有一个很大的特点,就是可以指定锁是公平锁还是非公平锁,公平锁表示线程获取锁的顺序是按照线程排队的顺序来分配的,而非公平锁就是一种获取锁的抢占机制,是随机获得 ...
- 死磕 java同步系列之ReentrantLock源码解析(一)——公平锁、非公平锁
问题 (1)重入锁是什么? (2)ReentrantLock如何实现重入锁? (3)ReentrantLock为什么默认是非公平模式? (4)ReentrantLock除了可重入还有哪些特性? 简介 ...
- ReentrantLock源码探究1:非公平锁的获取和释放
1.AQS简单介绍 Sync是ReentrantLock的一个内部类,它继承了AbstractQueuedSynchronizer,即AQS,在CountDownLatch.FutureTask. ...
- 深入了解ReentrantLock中的公平锁和非公平锁的加锁机制
ReentrantLock和synchronized一样都是实现线程同步,但是像比synchronized它更加灵活.强大.增加了轮询.超时.中断等高级功能,可以更加精细化的控制线程同步,它是基于AQ ...
- 深入分析ReentrantLock公平锁和非公平锁的区别
在ReentrantLock中包含了公平锁和非公平锁两种锁,通过查看源码可以看到这两种锁都是继承自Sync,而Sync又继承自AbstractQueuedSynchronizer,而AbstractQ ...
- Java之ReentrantLock公平锁和非公平锁
在Java的ReentrantLock构造函数中提供了两种锁:创建公平锁和非公平锁(默认).代码如下: public ReentrantLock() { sync = new NonfairSync( ...
- 深入分析ReentrantLock公平锁和非公平锁的区别 (转)
在ReentrantLock中包含了公平锁和非公平锁两种锁,通过查看源码可以看到这两种锁都是继承自Sync,而Sync又继承自AbstractQueuedSynchronizer,而AbstractQ ...
- ReentrantLock中的公平锁与非公平锁
简介 ReentrantLock是一种可重入锁,可以等同于synchronized的使用,但是比synchronized更加的强大.灵活. 一个可重入的排他锁,它具有与使用 synchronized ...
- 聊聊ReentrantLock基于AQS的公平锁和非公平锁的实现区别
ReentrantLock锁的实现是基于AQS实现的,所以先简单说下AQS: AQS是AbstractQueuedSynchronizer缩写,顾名思义:抽象的队列同步器,它是JUC里面许多同步工具类 ...
随机推荐
- [GO]字符串的使用
package main import ( "fmt" "strings" ) func main() { //判断字符串1是否包含字符串2,如果包含则返回tr ...
- linux 删除文件,某个文件例外
# shopt -s extglob (打开extglob模式) # rm -fr !(file1)
- firefox ubuntu 中文包
sudo apt-get install firefox-locale-zh-hans
- (转)走进AngularJs(六) 服务
原文地址:http://www.cnblogs.com/lvdabao/p/3464015.html 今天学习了一下ng的service机制,作为ng的基本知识之一,有必要做一个了解,在此做个笔记记录 ...
- CodeForces 47E. Cannon(离线暴力+数学)
E. Cannon time limit per test 3 seconds memory limit per test 256 megabytes input standard input out ...
- PyCharm创建普通项目配置支持jinja2语法
打开项目的根目录的.idea文件夹中项目名.iml文件(隐藏文件) 打开这个iml文件,在component标签的同级,添加如下代码: <component name="Templat ...
- C#winform自定义滚动条
1.控件 一个UserControl作为ScrollBg,一个panel作为ScrollBar 2.实现功能 (1)设置滚动条背景颜色和背景图片 (2)设置滚动条滑块的背景颜色和背景图片 (3)鼠标左 ...
- SqlServer发布订阅
我们在开发系统的时候,经常会遇到高并发的问题,还有高可用性和安全性方面的考虑,需要用读写分离的方案来解决问题.也就是在我们使用数据库比较多,更新少而查询比较多的情况下使用读写分离,实现提高性能,减少数 ...
- VMware Workstation内存不足问题的解决!
我今天使用VMware Workstation,遇到内存使用不足的问题,我使用的VMware Workstation是9,刚开始我以为是我的VMware Workstation版本低,所以上网找到了V ...
- 原生态js回顶部
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...