ReentrantLock中的公平锁与非公平锁
简介
ReentrantLock是一种可重入锁,可以等同于synchronized的使用,但是比synchronized更加的强大、灵活。
一个可重入的排他锁,它具有与使用 synchronized 方法和语句所访问的隐式监视器锁定相同的一些基本行为和语义,但功能更强大。ReentrantLock 将由最近成功获得锁定,并且还没有释放该锁定的线程所拥有。当锁定没有被另一个线程所拥有时,调用 lock 的线程将成功获取该锁定并返回。如果当前线程已经拥有该锁定,此方法将立即返回。可以使用 isHeldByCurrentThread() 和 getHoldCount() 方法来检查此情况是否发生。
内部实现
ReentrantLock内部拥有一个Sync内部类,该内部类继承自AQS,该内部类有两个子类FairSync和NonfairSync,分别代表了公平锁和非公平锁,ReentrantLock默认使用非公平锁
那么ReentrantLock内部的公平锁和非公平锁有什么区别呢?
区别主要在于两种锁实现的获取锁的方式,tryAcquire方法实现的不同:
首先来看看非公平锁:
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();//获取state
if (c == 0) {//state == 0表示锁没有被任何线程持有
if (compareAndSetState(0, acquires)) {//尝试设置state,如果设置成功,则获取到锁
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;//否则,获取锁失败
}
可以看到,非公平锁获取锁的过程是:首先判断当前锁是否被其他线程持有,如果是,直接返回失败,否则尝试获取锁
再来看看公平锁的加锁过程:
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
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;
}
对比上面的非公平锁的加锁过程,可以看到公平锁多了一个hasQueuedPredecessors方法的判断,来看看该方法的实现:
public final boolean hasQueuedPredecessors() {
// The correctness of this depends on head being initialized
// before tail and on head.next being accurate if the current
// thread is first in queue.
Node t = tail; // Read fields in reverse initialization order
Node h = head;
Node s;
return h != t &&
((s = h.next) == null || s.thread != Thread.currentThread());
}
其实就是判断当前线程是否为CLH队列的头节点
我们知道,尝试获取锁失败的线程都会被放入到CLH队列中,然后自旋尝试获取锁。对比公平锁和非公平锁的获取方式可以看到,公平锁之所以公平,是因为后续的线程必须进入到CLH同步队列中排队等候获取锁,但是分公平锁不需要,如果某个线程尝试获取锁的时候当前锁刚好被释放掉,那么它可以直接尝试获取锁,如果获取锁成功,直接执行,获取失败时,才进入到CLH同步队列
总结
通过上面的分析可以看到,公平锁是将所有线程依次放入CLH同步队列,然后再从队列中依次取出来执行;而非公平锁是部分已经进入同步队列的线程会像公平锁一样获取锁,但是其他尚且没有进入同步队列的线程可以与CLH同步队列中的首节点线程竞争锁;这也是为什么非公平锁性能比公平锁性能更佳的原因
ReentrantLock中的公平锁与非公平锁的更多相关文章
- java多线程20 : ReentrantLock中的方法 ,公平锁和非公平锁
公平锁与非公平锁 ReentrantLock有一个很大的特点,就是可以指定锁是公平锁还是非公平锁,公平锁表示线程获取锁的顺序是按照线程排队的顺序来分配的,而非公平锁就是一种获取锁的抢占机制,是随机获得 ...
- 深入了解ReentrantLock中的公平锁和非公平锁的加锁机制
ReentrantLock和synchronized一样都是实现线程同步,但是像比synchronized它更加灵活.强大.增加了轮询.超时.中断等高级功能,可以更加精细化的控制线程同步,它是基于AQ ...
- Java中的公平锁和非公平锁实现详解
前言 Java语言中有许多原生线程安全的数据结构,比如ArrayBlockingQueue.CopyOnWriteArrayList.LinkedBlockingQueue,它们线程安全的实现方式并非 ...
- 深入分析ReentrantLock公平锁和非公平锁的区别
在ReentrantLock中包含了公平锁和非公平锁两种锁,通过查看源码可以看到这两种锁都是继承自Sync,而Sync又继承自AbstractQueuedSynchronizer,而AbstractQ ...
- Java之ReentrantLock公平锁和非公平锁
在Java的ReentrantLock构造函数中提供了两种锁:创建公平锁和非公平锁(默认).代码如下: public ReentrantLock() { sync = new NonfairSync( ...
- 第五章 ReentrantLock源码解析1--获得非公平锁与公平锁lock()
最常用的方式: int a = 12; //注意:通常情况下,这个会设置成一个类变量,比如说Segement中的段锁与copyOnWriteArrayList中的全局锁 final Reentrant ...
- 深入分析ReentrantLock公平锁和非公平锁的区别 (转)
在ReentrantLock中包含了公平锁和非公平锁两种锁,通过查看源码可以看到这两种锁都是继承自Sync,而Sync又继承自AbstractQueuedSynchronizer,而AbstractQ ...
- 理解ReentrantLock的公平锁和非公平锁
学习AQS的时候,了解到AQS依赖于内部的FIFO同步队列来完成同步状态的管理,当前线程获取同步状态失败时,同步器会将当前线程以及等待状态等信息构造成一个Node对象并将其加入到同步队列,同时会阻塞当 ...
- 死磕 java同步系列之ReentrantLock源码解析(一)——公平锁、非公平锁
问题 (1)重入锁是什么? (2)ReentrantLock如何实现重入锁? (3)ReentrantLock为什么默认是非公平模式? (4)ReentrantLock除了可重入还有哪些特性? 简介 ...
随机推荐
- 《代码大全2》读书笔记 Week2
<代码大全2>第四.五章 第四章“关键的‘构建’决策”主要有以下三要点:1.每种编程语言都有优点和缺点,程序员应根据需要选择编程语言,尽量选择熟悉的语言以提高生产效率.作为一种表达工具,编 ...
- 如何将sql查询出的列名用注释代替?
如何将sql查询出的列名用注释代替? 大家正常的工作的时候,会有这样的要求,客户想要看下原始数据,但是呢.前台导出又麻烦,这时候只能从数据库拷贝出来一份.但是呢,数据库里面的字段客户又看不明白,只能用 ...
- shell command to replace UltraEdit
cat abc.txt |hexdump -C cat abc.txt |hexdump -C
- Ubuntu16.04 使用PPA安装JDK8
安装Java 8 ( 支持 Ubuntu 10.04 - Ubuntu 16.04 ) 1.如果你在 Ubuntu 软件中心安装过 OpenJDK,请先使用如下命令将其删除: sudo apt-get ...
- quartz的使用(一)
1.quartz是什么? Quartz 是一个完全由 Java 编写的开源作业调度框架,支持各种灵活的应用方式,并同时支持分布式和集群能力.2.quartz的基本要素 2.quartz的基本要素 Sc ...
- 初始化workbook时可能忽略的问题
正常情况下解析excel 先初始化workbook,使用文件名称后缀来初始化的. 一般情况下 这种是没有问题的,但是当遇到如果是07版本的 xlsx结尾的文件 改了后缀 为xls后 解析就会发生异常 ...
- phpstrom 注释效果
/** * .,:,,, .::,,,::. * .::::,,;;, .,;;:,,....:i: * :i,.::::,;i:. ....,,:::::::::,.... .;i:,. ..... ...
- php Excel导出id
<form action="{:U('Index/files')}" method="post" enctype="multipart/form ...
- delphi JPG转为BMP存入数据库
delphi JPG转为BMP存入数据库 必须在uses中引用JPEG procedure TForm1.BitBtn3Click(Sender: TObject);varjpg:TJPEGim ...
- Go 转义字符
Go 转义字符 package main import "fmt" func main() { fmt.Printf("Hello\tWorld!") } 本文 ...