阿里面试官:说一下公平锁和非公平锁的区别?

article/2025/9/27 8:31:15

点赞再看,养成习惯,微信搜索【三太子敖丙】关注这个互联网苟且偷生的工具人。

本文 GitHub https://github.com/JavaFamily 已收录,有一线大厂面试完整考点、资料以及我的系列文章。

前言

上次我们提到了乐观锁和悲观锁,那我们知道锁的类型还有很多种,我们今天简单聊一下,公平锁和非公平锁两口子,以及他们在我们代码中的实践。

正文

开始聊之前,我先大概说一下他们两者的定义,帮大家回顾或者认识一下。

公平锁:多个线程按照申请锁的顺序去获得锁,线程会直接进入队列去排队,永远都是队列的第一位才能得到锁。

  • 优点:所有的线程都能得到资源,不会饿死在队列中。
  • 缺点:吞吐量会下降很多,队列里面除了第一个线程,其他的线程都会阻塞,cpu唤醒阻塞线程的开销会很大。

非公平锁:多个线程去获取锁的时候,会直接去尝试获取,获取不到,再去进入等待队列,如果能获取到,就直接获取到锁。

  • 优点:可以减少CPU唤醒线程的开销,整体的吞吐效率会高点,CPU也不必取唤醒所有线程,会减少唤起线程的数量。
  • 缺点:你们可能也发现了,这样可能导致队列中间的线程一直获取不到锁或者长时间获取不到锁,导致饿死。

我举个例子给他家通俗易懂的讲一下的,想了好几天终于在前天跟三歪去肯德基买早餐排队的时候发现了怎么举例了。

现在是早餐时间,敖丙想去kfc搞个早餐,发现有很多人了,一过去没多想,就乖乖到队尾排队,这样大家都觉得很公平,先到先得,所以这是公平锁咯。

那非公平锁就是,敖丙过去买早餐,发现大家都在排队,但是敖丙这个人有点渣的,就是喜欢插队,那他就直接怼到第一位那去,后面的鸡蛋,米豆都不行,我插队也不敢说什么,只能默默忍受了。

但是偶尔,鸡蛋也会崛起,叫我滚到后面排队,我也是欺软怕硬,默默到后面排队,就插队失败了。

介绍完简单的例子,大家可能会说,渣丙,这个我也知道的啊。

我们是不是应该回归真正的实现了,其实在大家经常使用的ReentrantLock中就有相关公平锁,非公平锁的实现了。

大家还记得我在乐观锁、悲观锁章节提到的Sync类么,是ReentrantLock他本身的一个内部类,他继承了AbstractQueuedSynchronizer,我们在操作锁的大部分操作,都是Sync本身去实现的。

Sync呢又分别有两个子类:FairSync和NofairSync

他们子类的名字就可以见名知意了,公平和不公平那又是怎么在代码层面体现的呢?

公平锁:

你可以看到,他加了一个hasQueuedPredecessors的判断,那他判断里面有些什么玩意呢?

代码的大概意思也是判断当前的线程是不是位于同步队列的首位,是就是返回true,否就返回false。

我总觉得写到这里就应该差不多了,但是我坐下来,静静的思考之后发现,还是差了点什么。

上次聊过ReentrantLock了,但是AQS什么的我都只是提了一嘴,一个线程进来,他整个处理链路到底是怎样的呢?

公平锁到底公平不公平呢?让我们一起跟着丙丙走进ReentrantLock的内心世界。

上面提了这么多,我想你应该是有所了解了,那一个线程进来ReentrantLock这个渣男是怎么不公平的呢?(默认是非公平锁)

我先画个图,帮助大家了解下细节:

ReentrantLock的Sync继承了AbstractQueuedSynchronizer也就是我们常说的AQS

他也是ReentrantLock加锁释放锁的核心,大致的内容我之前一期提到了,我就不过多赘述了,他们看看一次加锁的过程吧。

A线程准备进去获取锁,首先判断了一下state状态,发现是0,所以可以CAS成功,并且修改了当前持有锁的线程为自己。

这个时候B线程也过来了,也是一上来先去判断了一下state状态,发现是1,那就CAS失败了,真晦气,只能乖乖去等待队列,等着唤醒了,先去睡一觉吧。

A持有久了,也有点腻了,准备释放掉锁,给别的仔一个机会,所以改了state状态,抹掉了持有锁线程的痕迹,准备去叫醒B。

这个时候有个带绿帽子的仔C过来了,发现state怎么是0啊,果断CAS修改为1,还修改了当前持有锁的线程为自己。

B线程被A叫醒准备去获取锁,发现state居然是1,CAS就失败了,只能失落的继续回去等待队列,路线还不忘骂A渣男,怎么骗自己,欺骗我的感情。

诺以上就是一个非公平锁的线程,这样的情况就有可能像B这样的线程长时间无法得到资源,优点就是可能有的线程减少了等待时间,提高了利用率。

现在都是默认非公平了,想要公平就得给构造器传值true。

ReentrantLock lock = new ReentrantLock(true);

说完非公平,那我也说一下公平的过程吧:

线A现在想要获得锁,先去判断下state,发现也是0,去看了看队列,自己居然是第一位,果断修改了持有线程为自己。

线程b过来了,去判断一下state,嗯哼?居然是state=1,那cas就失败了呀,所以只能乖乖去排队了。

未命名文件 (https://tva1.sinaimg.cn/large/00831rSTly1gcxaojuen2j30oa0jxgmh.jpg)
未命名文件 (https://tva1.sinaimg.cn/large/00831rSTly1gcxaojuen2j30oa0jxgmh.jpg)

线程A暖男来了,持有没多久就释放了,改掉了所有的状态就去唤醒线程B了,这个时候线程C进来了,但是他先判断了下state发现是0,以为有戏,然后去看了看队列,发现前面有人了,作为新时代的良好市民,果断排队去了。

线程B得到A的召唤,去判断state了,发现值为0,自己也是队列的第一位,那很香呀,可以得到了。

总结:

总结我不说话了,但是去获取锁判断的源码,箭头所指的位置,现在是不是都被我合理的解释了,当前线程,state,是否是0,是否是当前线程等等,都去思考下。

鬼知道我为了画图,画了多少费稿,点个赞过分么?

课后作业

公平锁真的公平么?那什么层面不是绝对的公平,什么层面才能算公平?

我是敖丙,一个在互联网苟且偷生的工具人。

最好的关系是互相成就,各位的「三连」就是丙丙创作的最大动力,我们下期见!

文章持续更新,可以微信搜索「 三太子敖丙 」第一时间阅读,回复【资料】【面试】【简历】有我准备的一线大厂面试资料和简历模板,本文 GitHub https://github.com/JavaFamily 已经收录,有大厂面试完整考点,欢迎Star。


http://chatgpt.dhexx.cn/article/GC7Y84uO.shtml

相关文章

Ubuntu 手动安装 JDK8

文章目录 1. 下载2. 解压安装3. 配置环境变量 1. 下载 先去官网下载合适的版本,官网:https://www.oracle.com/java/technologies/downloads/archive/ 通过下载页面获取到下载链接后,可以直接在Ubuntu上使用wget下载,也可以先下载…

centos8安装jdk教程

文章目录 一、安装二、配置环境变量三.验证 一、安装 1、查看JDK软件包列表 yum search java | grep -i --color jdk2、选择版本安装 yum install -y java-1.8.0-openjdk java-1.8.0-openjdk-devel或者如下命令安装jdk8所有文件 yum install -y java-1.8.0-openjdk*二、配置…

Java - JDK8安装及配置环境变量教程

Java - JDK8安装及配置环境变量教程 一、安装JDK教程 甲骨文官网下载JDK版本:windows64下载地址 下载完成后开始安装JDK:双击打开 点击下一步: 若不需要自定义路径,则安装到默认路径即可(安装的路径需记住&#xff0…

JDK8安装和环境配置

JDK8的安装和环境配置 一、JDK8下载二、安装三、环境配置 一、JDK8下载 官网下载: https://www.oracle.com/java/technologies/downloads/#java8-windows 二、安装 打开安装,一直下一步即可,可以在安装过程中更改安装地址,我放…

Java JDK 8的安装与配置

文章目录 前言1. 安装JDK 8Step1:选择JDK的版本Step2:选择系统平台Step3:下载安装包Step4:开始安装 2. 配置JDK 8Step1:配置“环境变量path” 前言 本教程是在Windows 64位平台上安装JDK 8版本。 1. 安装JDK 8 官网…

JDK8安装与环境配置

前言:在网上看了下JDK的安装与环境配置,发现很多视频以及博客的讲的都很复杂,对初学者很不友好。很多小白看到这些配置步骤都一脸懵,即使一步一步看着操作还是配置失败。这主要是因为很多博主配置了不需要的配置环境,让…

Linux安装JDK8详细图文教程

第一步、获取JDK文件 JDK下载包:直接进入 如果跳转登录页面,注册一个账号登录即可 登录过后文件就下载完成 第二步、上传JDK到服务器 1、创建JDK目录 mkdir -p /developer/env/jdk进入目录 cd /developer/env/jdk2、安装lrzsz(用于远程传…

JDK8安装教程(Windows、Ubuntu)

文章目录 下载JDK8在Windows上安装设置环境变量 在Ubuntu上安装 下载JDK8 Linux版,CSDN下载地址 推荐官网下载(需要注册),能下到Windows和Ubuntu的最新版 在Windows上安装 双击打开下载好的安装包,点击下一步 点击左…

linux系统安装jdk8详细教程

文章目录 前言一、下载jdk8的安装包二、压缩包上传解压1.将下载好的压缩包使用ftp工具上传到服务器2.将压缩包解压到指定目录 三、配置jdk的环境变量四、测试是否安装成功 前言 虚拟机版本:centos7 jdk版本:1.8 一、下载jdk8的安装包 方法1&#xff1a…

jdk8安装教程及环境变量配置

目录 一、JDK下载 二、安装JDK 三、环境变量配置 四、测试环境变量 一、JDK下载 1、JDK下载地址:Java Downloads | Oraclehttps://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html 2、选这个位置 3、向下滑动鼠标&#xff0c…

WIN10javaJDK8安装教程

一、下载jdk8文件 下载地址: http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html 下载步骤: 1、进入网站后往下拉,找到下载界面,选择自己合适的下载。 二、安装 双击运行刚下载好的exe文件…

JDK8安装教程-极其详细

下载安装JDK 前往oracle官网下载JDK 链接:https://www.oracle.com/java/technologies/downloads/#java8-windows下载完成后 打开下载的程序进行安装JDK 这里可以选择你要存放的位置 选择jre安装的路径 继续下一步,等待安装完成 配置环境变量 此电脑…

jdk8安装教程及环境变量部署

一、jdk下载 下载方式: 官网下载 地址:Java Downloads | Oracle网盘下载:链接:https://pan.baidu.com/s/1jjcOsdyaLfAy2N5zp64sew 提取码:tzsj 官网下载: 这是最新版本的可以根据自己的需要选择 选择W…

win10系统安装jdk8全过程

一 下载安装文件 jdk的安装与配置是Java学习的第一步,下面记录一下具体过程。首先根据自己系统下载对应版本。下载地址http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html 二 安装 双击exe文件,按照默认设置一步一步…

JDK8下载安装及配置环境教程,一看就会

目录 一.下载安装包 二.安装步骤 三.配置环境 一.下载安装包 百度网盘链接:百度网盘 请输入提取码 提取码:QI20 也可以选择自己到ORACLE官网下载:www.oracle.com. 1.点击链接进入官网 可以看到上方有个"Products",点击它 2.然后可以看到下滑的菜单…

JDK8详细图文安装教程

1、双击软件 2、点击下一步 3、选择jdk的安装目录 4、安装jre环境,选择jre安装目录 5、配置环境变量:选中计算机→右键→属性→高级系统设置→高级→环境变量 6、系统变量→新建JAVA_HOME变量 7、系统变量→寻找Path变量→编辑 8、新增jdk、jre的bin位置…

手把手教你安装JDK8~

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 JDK8安装教程一、JKD8下载及安装流程1、浏览器搜索jdk8,进入官网网站进行下载2、选择相应操作系统进行下载,(作者是windows系统&…

JDK8安装教程

曾几何时,我安装JAVA环境的时候,需要一遍一遍的对照着百度教程 配置环境变量。而且还经常出问题,只是这个环境变量的配置估计就劝退了一大帮人。 随着学习的深入以及后面JAVA版本的更新,突然发现,JAVA是不需要手动配置…

(超详细)2022年最新版java 8( jdk1.8u321)安装教程

2022年最新版java 8( jdk1.8u321)下载及安装 JunLeon——go big or go home 目录 2022年最新版java 8( jdk1.8u321)下载及安装 一、环境准备 jdk下载 二、jdk安装 三、配置环境变量 1、配置Java 8的环境变量 2、验证是否安…

Shell常用命令-- cut命令详解

一、cut命令 cut命令通常用来截取,以下是cut命令的一些用法: cut -d 指定分隔符 cut -f 指定列 cut -c 指定截取的字符二、示例 cut -d : -f 1 passwd # 以 : 为分隔符,截取文件的第一列cut -d : -f 1,6 pas…