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除了可重入还有哪些特性? 简介 ...
随机推荐
- linux更新grub内核启动参数的方法
#!/bin/bash set -x set -e export PS4=+{$LINENO:${FUNCNAME[0]}} trap 'echo "---NEWKERNARGS=$NEWK ...
- 前端 css 进阶
内容目录: 1.css盒子模型 2.浮动 float 3.溢出 overflow 4.定位 position 5. z-index 6.opacity 1.css盒子模型 margin:用来调节盒子与 ...
- day01 python起源 介绍 解释器 变量 流程控制if
day01 python 一.python的起源 1.python简介 java 企业级应用, android, app c 操作系统, 做开发语言的, 游戏的内核 ...
- Spring框架4大原则和主要功能
Spring框架4大原则: 使用POJO进行轻量级和最小侵入式开发 POJO(Plain Ordinary Java Object)简单的Java对象,实际就是普通JavaBeans,是为了避免和EJ ...
- Visio2016专业版永久激活码
Visio2016专业版永久激活码: [Key]:NKVJM-8MTT4-8YDFR-6738M-DPFJH [Key]:W9WC2-JN9W2-H4CBV-24QR7-M4HB8 [Key]:7K8 ...
- where I will go
为什么我选择了java? 作为一个0基础入门的编程小白,大一的时候我接触了C语言,写了我的第一个程序hello world,内心有点小骄傲(我也能编程了),那时候还不知道java是什么,然而实际公司的 ...
- CM自动化安装CDH5.14.0详解
CDH5.14.0版本说明 CDH最早版本只包含hadoop.hive.hbase等基础组件,CDH5.14.0版本目前已经封装了spark.impala.kudu(CDH 5.13.x开始)等众多组 ...
- CommandLineToArgvW调EXE传入参数【转载】
#include <afxwin.h> // TODO: add your code here LPWSTR *szArglist = NULL; ; szArglist = Comma ...
- NX二次开发-获取当前part所在路径UF_PART_ask_part_name
#include <uf.h> #include <uf_ui.h> #include <uf_part.h> #include <atlstr.h> ...
- iOS开发事件分发机制—响应链—手势影响
1.提纲 什么是iOS的事件分发机制 ? 一个事件UIEvent又是如何响应的? 手势对于响应链有何影响? 2.事件分发机制 2.1.来源 以直接触摸事件为例: 当用户一个手指触摸屏幕是会生成一个UI ...