前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >看完你就明白的锁系列之锁的公平性

看完你就明白的锁系列之锁的公平性

作者头像
cxuan
发布于 2019-10-31 12:51:54
发布于 2019-10-31 12:51:54
1K00
代码可运行
举报
文章被收录于专栏:Java建设者Java建设者
运行总次数:0
代码可运行

这是Java建设者的第 41 篇原创文章

这是看完你就明白的锁系列的第四篇文章

文章一览请看这里

看完你就应该能明白的悲观锁和乐观锁

看完你就明白的锁系列之自旋锁

看完你就明白的锁系列之锁的状态

此篇文章我们来探讨一下什么是锁的公平性

锁的公平性与非公平性

我们知道,在并发环境中,多个线程需要对同一资源进行访问,同一时刻只能有一个线程能够获取到锁并进行资源访问,那么剩下的这些线程怎么办呢?这就好比食堂排队打饭的模型,最先到达食堂的人拥有最先买饭的权利,那么剩下的人就需要在第一个人后面排队,这是理想的情况,即每个人都能够买上饭。那么现实情况是,在你排队的过程中,就有个别不老实的人想走捷径,插队打饭,如果插队的这个人后面没有人制止他这种行为,他就能够顺利买上饭,如果有人制止,他就也得去队伍后面排队。

对于正常排队的人来说,没有人插队,每个人都在等待排队打饭的机会,那么这种方式对每个人来说都是公平的,先来后到嘛。这种锁也叫做公平锁。

那么假如插队的这个人成功买上饭并且在买饭的过程不管有没有人制止他,他的这种行为对正常排队的人来说都是不公平的,这在锁的世界中也叫做非公平锁。

那么我们根据上面的描述可以得出下面的结论

公平锁表示线程获取锁的顺序是按照线程加锁的顺序来分配的,即先来先得的FIFO先进先出顺序。而非公平锁就是一种获取锁的抢占机制,是随机获得锁的,和公平锁不一样的就是先来的不一定先得到锁,这个方式可能造成某些线程一直拿不到锁,结果也就是不公平的了。

锁公平性的实现

在 Java 中,我们一般通过 ReetrantLock 来实现锁的公平性

我们分别通过两个例子来讲解一下锁的公平性和非公平性

锁的公平性

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class MyFairLock extends Thread{

    private ReentrantLock lock = new ReentrantLock(true);
    public void fairLock(){
        try {
            lock.lock();
            System.out.println(Thread.currentThread().getName()  + "正在持有锁");
        }finally {
            System.out.println(Thread.currentThread().getName()  + "释放了锁");
            lock.unlock();
        }
    }

    public static void main(String[] args) {
        MyFairLock myFairLock = new MyFairLock();
        Runnable runnable = () -> {
            System.out.println(Thread.currentThread().getName() + "启动");
            myFairLock.fairLock();
        };
        Thread[] thread = new Thread[10];
        for(int i = 0;i < 10;i++){
            thread[i] = new Thread(runnable);
        }
        for(int i = 0;i < 10;i++){
            thread[i].start();
        }
    }
}

我们创建了一个 ReetrantLock,并给构造函数传了一个 true,我们可以查看 ReetrantLock 的构造函数

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public ReentrantLock(boolean fair) {
  sync = fair ? new FairSync() : new NonfairSync();
}

根据 JavaDoc 的注释可知,如果是 true 的话,那么就会创建一个 ReentrantLock 的公平锁,然后并创建一个 FairSync ,FairSync 其实是一个 Sync 的内部类,它的主要作用是同步对象以获取公平锁。

而 Sync 是 ReentrantLock 中的内部类,Sync 继承 AbstractQueuedSynchronizer 类,AbstractQueuedSynchronizer 就是我们常说的 AQS ,它是 JUC(java.util.concurrent) 中最重要的一个类,通过它来实现独占锁和共享锁。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
abstract static class Sync extends AbstractQueuedSynchronizer {...}

也就是说,我们把 fair 参数设置为 true 之后,就可以实现一个公平锁了,是这样吗?我们回到示例代码,我们可以执行一下这段代码,它的输出是顺序获取的(碍于篇幅的原因,这里就暂不贴出了),也就是说我们创建了一个公平锁

锁的非公平性

与公平性相对的就是非公平性,我们通过设置 fair 参数为 true,便实现了一个公平锁,与之相对的,我们把 fair 参数设置为 false,是不是就是非公平锁了?用事实证明一下

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
private ReentrantLock lock = new ReentrantLock(false);

其他代码不变,我们执行一下看看输出(部分输出)

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Thread-1启动
Thread-4启动
Thread-1正在持有锁
Thread-1释放了锁
Thread-5启动
Thread-6启动
Thread-3启动
Thread-7启动
Thread-2启动

可以看到,线程的启动并没有按顺序获取,可以看出非公平锁对锁的获取是乱序的,即有一个抢占锁的过程。也就是说,我们把 fair 参数设置为 false 便实现了一个非公平锁。

公平锁的原理

接下来,我们通过 ReentrantLock 源码来讲解公平锁和非公平锁。首先先来认识一下

ReentrantLock 是什么

ReentrantLock 基本概述

ReentrantLock 是一把可重入锁,也是一把互斥锁,它具有与 synchronized 相同的方法和监视器锁的语义,但是它比 synchronized 有更多可扩展的功能。

ReentrantLock 的可重入性是指它可以由上次成功锁定但还未解锁的线程拥有。当只有一个线程尝试加锁时,该线程调用 lock() 方法会立刻返回成功并直接获取锁。如果当前线程已经拥有这把锁,这个方法会立刻返回。可以使用 isHeldByCurrentThreadgetHoldCount 进行检查。

这个类的构造函数接受可选择的 fairness 参数,当 fairness 设置为 true 时,在多线程争夺尝试加锁时,锁倾向于对等待时间最长的线程访问,这也是公平性的一种体现。否则,锁不能保证每个线程的访问顺序,也就是非公平锁。与使用默认设置的程序相比,使用许多线程访问的公平锁的程序可能会显示较低的总体吞吐量(即较慢;通常要慢得多)。但是获取锁并保证线程不会饥饿的次数比较小。无论如何请注意:锁的公平性不能保证线程调度的公平性。因此,使用公平锁的多线程之一可能会连续多次获得它,而其他活动线程没有进行且当前未持有该锁。这也是互斥性 的一种体现。

也要注意的 tryLock() 方法不支持公平性。如果锁是可以获取的,那么即使其他线程等待,它仍然能够返回成功。

推荐使用下面的代码来进行加锁和解锁

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class MyFairLock {
  private final ReentrantLock lock = new ReentrantLock();

  public void m() {
    lock.lock();
    try {
      // ...
    } finally {
      lock.unlock()
    }
  }
}

ReentrantLock 锁通过同一线程最多支持2147483647个递归锁。尝试超过此限制会导致锁定方法引发错误。

ReentrantLock 如何实现锁公平性

我们在上面的简述中提到,ReentrantLock 是可以实现锁的公平性的,那么原理是什么呢?下面我们通过其源码来了解一下 ReentrantLock 是如何实现锁的公平性的

跟踪其源码发现,调用 Lock.lock() 方法其实是调用了 sync 的内部的方法

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
abstract void lock();

而 sync 是最基础的同步控制 Lock 的类,它有公平锁和非公平锁的实现。它继承 AbstractQueuedSynchronizer 即 使用 AQS 状态代表锁持有的数量。

lock 是抽象方法是需要被子类实现的,而继承了 AQS 的类主要有

我们可以看到,所有实现了 AQS 的类都位于 JUC 包下,主要有五类:ReentrantLockReentrantReadWriteLockSemaphoreCountDownLatchThreadPoolExecutor,其中 ReentrantLock、ReentrantReadWriteLock、Semaphore 都可以实现公平锁和非公平锁。

下面是公平锁 FairSync 的继承关系

非公平锁的NonFairSync 的继承关系

由继承图可以看到,两个类的继承关系都是相同的,我们从源码发现,公平锁和非公平锁的实现就是下面这段代码的区别(下一篇文章我们会从原理角度分析一下公平锁和非公平锁的实现)

通过上图中的源代码对比,我们可以明显的看出公平锁与非公平锁的lock()方法唯一的区别就在于公平锁在获取同步状态时多了一个限制条件:hasQueuedPredecessors()

hasQueuedPredecessors() 也是 AQS 中的方法,它主要是用来 查询是否有任何线程在等待获取锁的时间比当前线程长,也就是说每个等待线程都是在一个队列中,此方法就是判断队列中在当前线程获取锁时,是否有等待锁时间比自己还长的队列,如果当前线程之前有排队的线程,返回 true,如果当前线程位于队列的开头或队列为空,返回 false。

综上,公平锁就是通过同步队列来实现多个线程按照申请锁的顺序来获取锁,从而实现公平的特性。非公平锁加锁时不考虑排队等待问题,直接尝试获取锁,所以存在后申请却先获得锁的情况。

文章参考:

https://tech.meituan.com/2018/11/15/java-lock.html

https://www.jianshu.com/p/eaea337c5e5b

https://blog.csdn.net/oChangWen/article/details/77622889

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-10-29,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Java建设者 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
【Android 安装包优化】WebP 图片格式性能测试 ( 测试 WebP 图片解码速度 | 测试 WebP 图片编码速度 )
使用 【Android 安装包优化】WebP 图片格式 ( WebP 图片格式简介 | 使用 Android Studio 转换 WebP 图片格式 ) 二、使用 Android Studio 转换 WebP 图片格式 博客中的转换前后的图片 ;
韩曙亮
2023/03/29
1.3K0
【Android 安装包优化】WebP 图片格式性能测试 ( 测试 WebP 图片解码速度 | 测试 WebP 图片编码速度 )
【Android 安装包优化】WebP 应用 ( 4.0 以下兼容 WebP | Android Studio 中使用 libwebp.so 库向下兼容版本 | libwebp 库测试可用性 )
已经做好了兼容 4.0 以下系统的的 Android 工程源码 , 在下面博客资源中可以下载 ;
韩曙亮
2023/03/29
9960
【Android 安装包优化】WebP 应用 ( 4.0 以下兼容 WebP | Android Studio 中使用 libwebp.so 库向下兼容版本 | libwebp 库测试可用性 )
【Android 安装包优化】WebP 应用 ( Android 中使用 libwebp.so 库解码 WebP 图片 )
libwebp.jar 中解码相关的的方法如下 : libwebpJNI 是 Java 层调用 libwebp.so 动态库的入口类 ;
韩曙亮
2023/03/29
2K0
【Android 安装包优化】WebP 应用 ( Android 中使用 libwebp.so 库解码 WebP 图片 )
【Android Gradle 插件】自定义 Gradle 插件优化图片 ① ( Android 中的 WebP 图片格式使用 | WebP 格式转换 | WebP 参考文档 )
在 Android 安装包优化 专栏中 , 介绍了如何 给 Android APK 安装文件进行瘦身 , 介绍了
韩曙亮
2023/03/30
1.5K0
【Android Gradle 插件】自定义 Gradle 插件优化图片 ① ( Android 中的 WebP 图片格式使用 | WebP 格式转换 | WebP 参考文档 )
【Android 安装包优化】WebP 应用 ( Android 中使用 libwebp.so 库编码 WebP 图片 )
libwebp.jar 中编码相关的的方法如下 : libwebpJNI 是 Java 层调用 libwebp.so 动态库的入口类 ;
韩曙亮
2023/03/29
6840
【Android 安装包优化】WebP 图片转换 ( 使用 iSparta 转换 WebP 图片格式 | Google 提供的 libwebp 库 )
isparta 工具已经停止维护 , 最新的版本下载下来 , 无法使用 , 这里保留了一个之前可以使用的版本 , 下载 https://download.csdn.net/download/han1202012/17496041 博客资源 , 解压 iSparta-win64.zip 压缩包 , 运行其中的 iSparta.exe 可执行文件 , 即可使用该工具 ;
韩曙亮
2023/03/29
9690
【Android 安装包优化】WebP 图片转换 ( 使用 iSparta 转换 WebP 图片格式 | Google 提供的 libwebp 库 )
【Android 安装包优化】WebP 应用 ( libwebp 源码下载 | Android.mk 和 Application.mk 构建脚本修改 | libwebp 函数库编译 )
Google 提供了一系列的 WebP 相关工具和函数库 , 如 cwebp , dwebp , gif2webp , img2webp 等工具 , libwebp 函数库 ;
韩曙亮
2023/03/29
1.2K0
【Android 安装包优化】WebP 应用 ( libwebp 源码下载 | Android.mk 和 Application.mk 构建脚本修改 | libwebp 函数库编译 )
【Android 安装包优化】WebP 图片格式 ( WebP 图片格式简介 | 使用 Android Studio 转换 WebP 图片格式 )
WebP 是一种压缩度很高的文件格式 , 有 " 有损压缩 " 和 " 无损压缩 " 两种形式 ;
韩曙亮
2023/03/29
2.8K0
【Android 安装包优化】WebP 图片格式 ( WebP 图片格式简介 | 使用 Android Studio 转换 WebP 图片格式 )
【Android 安装包优化】Android 中使用 SVG 图片 ( 批量转换 SVG 格式图片为 Vector Asset 矢量图资源 )
在 【Android 安装包优化】Android 中使用 SVG 图片 ( SVG 矢量图简介 | Android 中生成 Vector 矢量图资源 ) 二、Android 中生成 Vector 矢量图资源 博客章节中 , 使用 Android Studio 中自带的 " Asset Studio " 工具将 SVG 格式的图片转为 Vector Asset 矢量图资源 , 但是每次只能转换一张 , 效率很低 ;
韩曙亮
2023/03/29
1.4K0
【Android 安装包优化】Android 中使用 SVG 图片 ( 批量转换 SVG 格式图片为 Vector Asset 矢量图资源 )
关于webp图片格式初探
前言 不管是 PC 还是移动端,图片一直是流量大头,以苹果公司 Retina 产品为代表的高 PPI 屏对图片的质量提出了更高的要求,如何保证在图片的精细度不降低的前提下缩小图片体积,成为了一个有价值且值得探索的事情。 但如今对于 JPEG、PNG 和 GIF 这些图片格式的优化几乎已经达到了极致, 若想改变现状开辟新局面,便要有釜底抽薪的胆量和气魄,而 Google 给了我们一个新选择:WebP。 对 WebP 的研究缘起于手机 QQ 原创表情商城,由于表情包体积较大,在 2G/3G 的网络环境
xiangzhihong
2018/02/01
5.6K0
关于webp图片格式初探
移动客户端WebP 图片格式优化
前言 在移动端,图片一直是流量大头,一些商品列表和详情等页面,图片大小动不动就以几百K,当然在某些比较大的公司会根据具体情况去加载相应尺寸的图片,这就意味着服务器必须提供多套尺寸的图片。而对传统的JPEG、PNG 和 GIF 这些图片格式的优化几乎已经达到了极致,而 Google在这个时候给了开发者一个新选择:WebP。在Google 的明星产品如 Youtube、Gmail、Google Play 中都可以看到 WebP 的身影,而 Chrome 网上商店甚至已完全使用了 WebP。国外公司如 Faceb
xiangzhihong
2018/01/26
1.7K0
【Android 安装包优化】Android 中使用 SVG 图片 ( 使用 appcompat 支持库兼容 5.0 以下版本的 Android 系统使用矢量图 )
满足上述版本要求后 , 在 build.gradle 构建脚本的 " android / defaultConfig " 层级下 , 添加矢量图支持 , vectorDrawables.useSupportLibrary = true ;
韩曙亮
2023/03/29
7620
【Android 安装包优化】Android 中使用 SVG 图片 ( 使用 appcompat 支持库兼容 5.0 以下版本的 Android 系统使用矢量图 )
【Android 安装包优化】Android 中使用 SVG 图片 ( Android 5.0 以下的矢量图方案 | 矢量图生成为 PNG 图片 )
Android 矢量图参考文档 : https://developer.android.google.cn/studio/write/vector-asset-studio
韩曙亮
2023/03/29
5750
【Android 安装包优化】Android 中使用 SVG 图片 ( Android 5.0 以下的矢量图方案 | 矢量图生成为 PNG 图片 )
WordPress 支持 WebP格式图片上传方法
WebP(发音:weppy)是一种同时提供了有损压缩与无损压缩(可逆压缩)的图片文件格式,派生自影像编码格式VP8,被认为是WebM多媒体格式的姊妹项目,是由Google在购买On2 Technologies后发展出来,以BSD授权条款发布。
阳光岛主
2019/12/30
2.3K0
WordPress 支持 WebP格式图片上传方法
9.27【前端开发】图片文件格式:常见的图片格式对比有何优劣以及如何使用Google的webp格式?
总结一下,对于色彩与图像内容比较丰富,变化比较多端的,适合使用jpg,例如大型背景、头像、人物照片等。对于颜色单一,有大色块的图像,例如图标等,适合用png,压缩效率高,并且有透明。小动画可以使用gif,便不适合使用复杂的视频。
LIYI
2020/10/09
2.5K0
9.27【前端开发】图片文件格式:常见的图片格式对比有何优劣以及如何使用Google的webp格式?
现代图片性能优化及体验优化指南 - 图片类型及 Picture 标签的使用
图片资源,在我们的业务中可谓是占据了非常大头的一环,尤其是其对带宽的消耗是十分巨大的。
Sb_Coco
2023/03/01
1.2K0
现代图片性能优化及体验优化指南 - 图片类型及 Picture 标签的使用
【Android 安装包优化】Android 应用中 7zr 可执行程序准备 ( Android Studio 导入可执行 7zr 程序 | 从 Assets 资源文件拷贝 7zr 到内置存储 )
上一篇博客 【Android 安装包优化】p7zip 源码交叉编译 Android 平台可执行程序 ( 下载 p7zip 源码 | 交叉编译 Android 中使用 7z 可执行程序 ) 编译了可以在 Android 平台执行的 7zr 可执行程序 ;
韩曙亮
2023/03/29
5970
【Android 安装包优化】Android 应用中 7zr 可执行程序准备  ( Android Studio 导入可执行 7zr 程序 | 从 Assets 资源文件拷贝 7zr 到内置存储 )
扒一扒“WEBP格式”的图片
HTML5学堂:谷歌于2010年推出的新一代图片格式 —— WEBP,随着移动互联网的发展,WEBP格式在2015年逐渐的开始被大公司部分采用。本文主要除了比较WEBP与JPG等传统格式的图片之外,还介绍了如何转换WEBP格式图片以及具体开发时的用法。 使用WEBP图片的目的 保证图片质量的前提下缩小图片体积。JPEG、PNG以及GIF这些格式的图片已经没有太大的优化空间。但是,WebP图片格式给图片优化提供了另一种可能 图片压缩 无损压缩的图片格式 TIFF;GIF;RAW;PCX;TAG;PNG;BMP
HTML5学堂
2018/03/13
3.2K0
扒一扒“WEBP格式”的图片
【Android 文件管理】分区存储 ( 创建与查询图片文件 )
在上一篇博客 【Android 文件管理】分区存储 ( MediaStore 文件操作 ) 中 , 创建了一个文本文件 , 并向其写出一个字符串 ;
韩曙亮
2023/03/29
1.2K0
【Android 文件管理】分区存储 ( 创建与查询图片文件 )
高性能图片优化方案
目录介绍01.图片基础概念介绍1.1 图片占用内存介绍1.2 加载网络图片流程1.3 三方库加载图片逻辑1.4 从网络直接拉取图片1.5 加载图片的流程1.6 Bitmap能直接存储吗1.7 Bitmap创建流程1.8 图片框架如何设计02.图片内存计算方式2.1 如何计算占用内存2.2 上面计算内存对吗2.3 一个像素占用内存2.4 使用API获取内存2.5 影响Bitmap内存因素2.6 加载xhdpi和xxhdpi图片2.7 图片一些注意事项03.大图的内存优化3.1 常见图片压缩3.2 图片尺寸压缩
杨充
2022/10/16
1K0
推荐阅读
【Android 安装包优化】WebP 图片格式性能测试 ( 测试 WebP 图片解码速度 | 测试 WebP 图片编码速度 )
1.3K0
【Android 安装包优化】WebP 应用 ( 4.0 以下兼容 WebP | Android Studio 中使用 libwebp.so 库向下兼容版本 | libwebp 库测试可用性 )
9960
【Android 安装包优化】WebP 应用 ( Android 中使用 libwebp.so 库解码 WebP 图片 )
2K0
【Android Gradle 插件】自定义 Gradle 插件优化图片 ① ( Android 中的 WebP 图片格式使用 | WebP 格式转换 | WebP 参考文档 )
1.5K0
【Android 安装包优化】WebP 应用 ( Android 中使用 libwebp.so 库编码 WebP 图片 )
6840
【Android 安装包优化】WebP 图片转换 ( 使用 iSparta 转换 WebP 图片格式 | Google 提供的 libwebp 库 )
9690
【Android 安装包优化】WebP 应用 ( libwebp 源码下载 | Android.mk 和 Application.mk 构建脚本修改 | libwebp 函数库编译 )
1.2K0
【Android 安装包优化】WebP 图片格式 ( WebP 图片格式简介 | 使用 Android Studio 转换 WebP 图片格式 )
2.8K0
【Android 安装包优化】Android 中使用 SVG 图片 ( 批量转换 SVG 格式图片为 Vector Asset 矢量图资源 )
1.4K0
关于webp图片格式初探
5.6K0
移动客户端WebP 图片格式优化
1.7K0
【Android 安装包优化】Android 中使用 SVG 图片 ( 使用 appcompat 支持库兼容 5.0 以下版本的 Android 系统使用矢量图 )
7620
【Android 安装包优化】Android 中使用 SVG 图片 ( Android 5.0 以下的矢量图方案 | 矢量图生成为 PNG 图片 )
5750
WordPress 支持 WebP格式图片上传方法
2.3K0
9.27【前端开发】图片文件格式:常见的图片格式对比有何优劣以及如何使用Google的webp格式?
2.5K0
现代图片性能优化及体验优化指南 - 图片类型及 Picture 标签的使用
1.2K0
【Android 安装包优化】Android 应用中 7zr 可执行程序准备 ( Android Studio 导入可执行 7zr 程序 | 从 Assets 资源文件拷贝 7zr 到内置存储 )
5970
扒一扒“WEBP格式”的图片
3.2K0
【Android 文件管理】分区存储 ( 创建与查询图片文件 )
1.2K0
高性能图片优化方案
1K0
相关推荐
【Android 安装包优化】WebP 图片格式性能测试 ( 测试 WebP 图片解码速度 | 测试 WebP 图片编码速度 )
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验