公平锁与非公平锁的核心原理

article/2025/9/27 6:18:47

前言:大家好,我是小威,24届毕业生,在一家满意的公司实习。本篇文章是关于并发编程中AQS的独占模式和共享模式。
本篇文章记录的基础知识,适合在学Java的小白,也适合复习中,面试中的大佬🤩🤩。
如果文章有什么需要改进的地方还请大佬不吝赐教👏👏。
小威在此先感谢各位大佬啦~~🤞🤞
在这里插入图片描述

🏠个人主页:小威要向诸佬学习呀
🧑个人简介:大家好,我是小威,一个想要与大家共同进步的男人😉😉
目前状况🎉:24届毕业生,在一家满意的公司实习👏👏

🎁如果大佬在准备面试,可以使用我找实习前用的刷题神器哦刷题神器点这里哟
💕欢迎大家:这里是CSDN,我总结知识的地方,欢迎来到我的博客,我亲爱的大佬😘

以下正文开始
在这里插入图片描述

文章目录

  • Lock锁接口方法
  • 公平锁简介
  • ReentranLock的公平锁
  • ReentranLock的非公平锁

Lock锁接口方法

前面了解到了synchronized锁,也知道了synchronized锁是一种JVM提供内置锁,但synchronized有一些缺点:比如不支持响应中断,不支持超时,不支持以非阻塞的方式获取锁等。而今天的主角Lock锁,需要我们手动获取锁和释放锁,里面有很多方式来获取锁,比如以阻塞方式获取锁,在指定时间内获取锁,非阻塞模式下抢占锁等,其方法源码如下(位于package java.util.concurrent.locks包下):

//Lock接口下的方法
public interface Lock {//阻塞式抢占锁,如果抢到锁,则向下执行程序;抢占失败线程阻塞,直到释放锁才会进行抢占锁void lock();//可中断模式抢占锁,线程调用此方法能够中断线程void lockInterruptibly() throws InterruptedException;//非阻塞式尝试获取锁,调用此方法线程不会阻塞,抢到锁返回true,失败返回falseboolean tryLock();//在指定时间内尝试获取锁,在指定时间内抢到锁成功返回true,失败返回falseboolean tryLock(long time, TimeUnit unit) throws InterruptedException;//释放锁,线程执行完程序后,调用此方法来释放锁资源void unlock();//条件队列Condition newCondition();}

在这里插入图片描述

公平锁简介

公平锁,顾名思义,所有线程获取锁都是公平的。在多线程并发情况下,线程争抢锁时,首先会检查等待队列中是否有其他线程在等待。如果等待队列为空,没有线程在等待,那么当前线程会拿到锁资源;如果等待队列中有其他线程在等待,那么当前线程会排到等待队列的尾部,好比我们排队买东西一样。

ReentranLock的公平锁

ReentranLock类实现了Lock接口,重写了里面的方法,对于ReentranLock类中的公平锁,我们可以看到如下源码:

    public ReentrantLock(boolean fair) {sync = fair ? new FairSync() : new NonfairSync();}

在上述构造方法中,新建锁对象是否为公平锁,在于传入的参数是true还是false,在三目运算符中,如果传入参数为true,则会创建一个FairSync()对象赋值给sync,线程获取的锁是公平锁;如果为false则创建一个NonfairSync()对象,线程获取的锁是非公平锁。点入FairSync()方法,得到如下源码:

    static final class FairSync extends Sync {private static final long serialVersionUID = -3000897897090466540L;final void lock() {acquire(1);}/*这里将acquire方法放于此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) {//首先会判断是否有前驱节点,如果没有就会调用CAS机制更新锁对象状态if (!hasQueuedPredecessors() &&compareAndSetState(0, acquires)) {//设置当前线程拥有锁资源setExclusiveOwnerThread(current);//如果获取锁资源成功则返回truereturn true;}}//或者如果拿到锁的就是当前线程else if (current == getExclusiveOwnerThread()) {//将锁的状态+1,考虑到锁重入int nextc = c + acquires;if (nextc < 0)//超过了最大锁的数量throw new Error("Maximum lock count exceeded");//如果锁数量没有溢出,设置当前锁状态setState(nextc);//如果成功获取锁资源则返回truereturn true;}//如果前两条都不符合,则返回falsereturn false;}}

上述代码涉及到了hasQueuedPredecessors()方法,返回true则代表有有前驱节点,点入查看得到以下源码:

    public final boolean hasQueuedPredecessors() {Node t = tail; // Read fields in reverse initialization orderNode h = head;Node s;//h!=t,首节点不等于尾节点表示队列中有节点return h != t &&(//s为头结点的下一个节点,返回false表示队列中还有第二个节点,或运算符后面表示,第二个线程不是当前线程(s = h.next) == null || s.thread != Thread.currentThread());}

因此做出总结,使用ReentranLock的公平锁,当线程调用ReentranLock类中的lock()方法时,会首先调用FairSync类中的lock()方法;然后FairSync类中的lock()方法会调用AQS类(AbstractQueuedSynchronizer)中的acquire(1)方法获取资源,前面的文章里也提到过,acquire()方法会调用tryAcquire()方法尝试获取锁,tryAcquire()方法里面没有具体的实现,tryAcquire()方法具体逻辑是由其子类实现的,因此调用的还是FairSync类中的方法。在AQS中的acquire()方法中如果尝试获取资源失败,会调用addWaiter()方法将当前线程封装为Node节点放到等待队列的尾部,并且调用AQS中的acquireQueued方法使线程在等待队列中排队。
在这里插入图片描述

ReentranLock的非公平锁

非公平锁就是所有抢占锁的线程都是不公平的,在我们日常生活中就相当于是插队现象,不过也与插队稍微不同。在多线程并发时,每个线程在抢占锁的过程中,都会先尝试获取锁,如果获取成功,则直接指向具体的业务逻辑;如果获取锁失败,则会像公平锁一样在等待队列队尾等待。

在非公平锁模式下,由于刚来的线程可以在队首位置进行一次插队,所以当插队成功时,后面的线程可能会出现长时间等待,无法获取锁资源产生饥饿现象。但是非公平锁性能比公平锁性能更好。

对于ReentranLock类中的非公平锁,实现方式有两种,一种是默认的构造方式,另一种是和上面的一样,源码如下:

    //第一种方式,默认实现public ReentrantLock() {sync = new NonfairSync();}//第二种方式,传入false来创建非公平锁对象public ReentrantLock(boolean fair) {sync = fair ? new FairSync() : new NonfairSync();}

对于创建的NonfairSync()类对象,其源码如下:

    static final class NonfairSync extends Sync {private static final long serialVersionUID = 7316153563782823691L;/*** Performs lock.  Try immediate barge, backing up to normal* acquire on failure.*/final void lock() {if (compareAndSetState(0, 1))setExclusiveOwnerThread(Thread.currentThread());elseacquire(1);}protected final boolean tryAcquire(int acquires) {return nonfairTryAcquire(acquires);}}

在这里插入图片描述

在这里插入图片描述

在上述代码中,当线程获取锁时,并没有直接将当前线程放入等待队列中,而是先尝试获取锁资源,如果获取锁成功,设置state标志位1成功,则直接将当前线程拿到锁,执行线程业务;如果获取锁资源失败,则调用AQS中的acquire方法获取资源,而acquire()方法会回调上面NonfairSyn类中的tryAcquire()方法,然后又会回调Sync类中的nonfairTryAcquire()方法(NonfairSync类继承了Sync类) ,点击nonfairTryAcquire(acquires)查看源码:

        final boolean nonfairTryAcquire(int acquires) {final Thread current = Thread.currentThread();int c = getState();if (c == 0) {if (compareAndSetState(0, acquires)) {setExclusiveOwnerThread(current);return true;}}else if (current == getExclusiveOwnerThread()) {int nextc = c + acquires;if (nextc < 0) // overflowthrow new Error("Maximum lock count exceeded");setState(nextc);return true;}return false;}

由上诉代码,没有将线程放入到等待队列中,只是对锁的状态进行了判断,若标识为0,代表没有线程拿到锁,当前线程会使用CAS机制改变锁状态,并调用setExclusiveOwnerThread(current)方法让当前线程拿到锁。
在这里插入图片描述

因此综上所述,在使用ReentranLock中的非公平锁时,首先会调用lock()方法,,而ReentranLock类中lock()方法会调用NonfairSync类中的lock()方法,接着NonfairSync类中的lock()方法会调用AQS中的acquire()方法来获取锁资源,AQS中的acquire()方法又会回调NonfairSync类中tryAcquire()方法尝试获取资源,NonfairSync类中tryAcquire()方法会调用Sync类中的nonfairTryAcquire方法尝试非公平锁获取资源;获取失败的话,AQS中的acquire()方法会调用addWaiter()方法将当前线程封装成Node节点放入到等待队列的队尾,而后AQS中的acquire()方法会调用AQS中的acquireQueued()方法让线程在等待队列中排队。这就是非公平锁的整个流程。

本篇文章就分享到这里了,后续将会分享各种其他关于并发编程的知识,感谢大佬认真读完支持咯 ~
在这里插入图片描述

文章到这里就结束了,如果有什么疑问的地方请指出,诸佬们一起讨论🍻
希望能和诸佬们一起努力,今后进入到心仪的公司
再次感谢各位小伙伴儿们的支持🤞

在这里插入图片描述


http://chatgpt.dhexx.cn/article/6Wvc1t5Q.shtml

相关文章

公平锁和非公平锁介绍,为什么要“非公平”?

什么是公平和非公平 公平锁指的是按照线程请求的顺序&#xff0c;来分配锁&#xff1b;而非公平锁指的是不完全按照请求的顺序&#xff0c;在一定情况下&#xff0c;可以允许插队。但需要注意这里的非公平并不是指完全的随机&#xff0c;不是说线程可以任意插队&#xff0c;而…

图解ReentrantLock公平锁和非公平锁实现

概述 ReentrantLock是Java并发中十分常用的一个类&#xff0c;具备类似synchronized锁的作用。但是相比synchronized, 它具备更强的能力&#xff0c;同时支持公平锁和非公平锁。 公平锁&#xff1a; 指多个线程按照申请锁的顺序来获取锁&#xff0c;线程直接进入队列中排队&a…

Java面试突击:公平锁和非公平锁有什么区别?

从公平的角度来说&#xff0c;Java 中的锁总共可分为两类&#xff1a;公平锁和非公平锁。但公平锁和非公平锁有哪些区别?孰优孰劣呢?在 Java 中的应用场景又有哪些呢?接下来我们一起来看。 正文公平锁&#xff1a;每个线程获取锁的顺序是按照线程访问锁的先后顺序获取的&am…

浅谈ReentrantLock的公平锁和非公平锁的区别

前言 最近在看java并发编程这本书&#xff0c;已经看了点ReentrantLock的源码&#xff0c;以及之前有面试官问&#xff0c;公平锁和非公平锁有啥区别&#xff0c;我就只是从源码层面说了一下区别&#xff0c;但在性能上也有区别&#xff0c;今天就来说道说道。 公平与非公平 …

aqs原理初探以及公平锁和非公平锁实现

深入理解AQS 一&#xff0c;AQS1&#xff0c;ReentrantLock2&#xff0c;CAS3&#xff0c;AbstractQueuedSynchronizer3.1&#xff0c;FairSync3.2&#xff0c;NofairSync3.3&#xff0c;AQS中几个重要的相关参数3.4&#xff0c;Node 一&#xff0c;AQS AbstractQueuedSynchro…

图解ReentrantLock底层公平锁和非公平锁实现原理

&#x1f4bb;在面试或者日常开发当中&#xff0c;经常会遇到公平锁和非公平锁的概念。 两者最大的区别如下&#x1f447; 1️⃣ 公平锁&#xff1a;N个线程去申请锁时&#xff0c;会按照先后顺序进入一个队列当中去排队&#xff0c;依次按照先后顺序获取锁。就像下图描述的上…

ReentrantLock之公平锁和非公平锁详解

ReentrantLock是一个互斥锁&#xff0c;它具有synchronized相同的能力&#xff1b;但相比之下&#xff0c;ReentrantLock扩展性更强&#xff0c;比如实现了公平锁。 下面详细拆解下ReentrantLock的公平锁和非公平锁的实现。 JDK版本&#xff1a;1.8.0_40 公平锁 先看Reentr…

ReentrantLock中公平锁和非公平锁的区别

目录 背景知识 ReentrantLock的组成 概述 公平锁示意图 非公平锁示意图 源码解读 非公平锁 公平锁 代码对比 问题 知识扩展 tryLock方法 参考资料 背景知识 ReentrantLock的组成 首先看下ReentrantLock的组成结构。 公平锁和非公平锁主要是通过内部类FairSync和…

公平锁和非公平锁

Reentrant Re entrant&#xff0c;Re是重复、又、再的意思&#xff0c;entrant是enter的名词或者形容词形式&#xff0c;翻译为进入者或者可进入的&#xff0c;所以Reentrant翻译为可重复进入的、可再次进入的&#xff0c;因此ReentrantLock翻译为重入锁或者再入锁。 公平锁…

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

点赞再看&#xff0c;养成习惯&#xff0c;微信搜索【三太子敖丙】关注这个互联网苟且偷生的工具人。 本文 GitHub https://github.com/JavaFamily 已收录&#xff0c;有一线大厂面试完整考点、资料以及我的系列文章。 前言 上次我们提到了乐观锁和悲观锁&#xff0c;那我们知道…

Ubuntu 手动安装 JDK8

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

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版本&#xff1a;windows64下载地址 下载完成后开始安装JDK&#xff1a;双击打开 点击下一步&#xff1a; 若不需要自定义路径&#xff0c;则安装到默认路径即可&#xff08;安装的路径需记住&#xff0…

JDK8安装和环境配置

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

Java JDK 8的安装与配置

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

JDK8安装与环境配置

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

Linux安装JDK8详细图文教程

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

JDK8安装教程(Windows、Ubuntu)

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

linux系统安装jdk8详细教程

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

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

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