面试之ReentrantLock

一,ReentrantLock

1.ReentrantLock是什么?

ReentrantLock实现了Lock接口,是一个可重入且独占式的锁,和Synchronized关键字类似,不过ReentrantLock更灵活,更强大,增加了轮询、超时、中断、公平锁和非公平锁等高级功能。

ReentrantLock 里面有一个内部类 ,Sync 继承 AQS(AbstractQueuedSynchronizer),添加锁和释放锁的大部分操作实际上都是在 Sync中实现的。Sync 有公平锁 FairSync 和非公平锁 NonfairSync 两个子类。

2.公平锁与非公平锁

  • 公平锁:先到先得原则。如果锁被线程释放之后,先申请的线程先得到锁。公平锁性能较差一些,因为公平锁为了保证事件上的绝对顺序,上下文切换频繁
  • 非公平锁:相对公平锁来说,如果锁被线程释放之后,后续所有申请的线程都有可能获得到锁,不会按照某一个顺序来,是随机的。非公平锁性能更高,但是可能导致某些线程永远获取不到锁

3.ReentrantLock的使用

lock(): 加锁 , 如果获取不到锁就死等 .
trylock( 超时时间 ): 加锁 , 如果获取不到锁 , 等待一定的时间之后就放弃加锁 .
unlock(): 解锁

4.ReentrantLock与Synchronized区别

  • Sychronized依赖于JVM,而ReentrantLock依赖于API:Synchronized 关键字进行了很多优化,但是这些优化都是在虚拟机层面实现的,并没有直接暴露给我们。ReentrantLock 是 JDK 层面实现的(也就是 API 层面,需要 lock() 和 unlock() 方法配合 try/finally 语句块来完成),所以我们可以通过查看它的源代码,来看它是如何实现的
  • Sychronized使用时不需要手动释放锁,ReentrantLock使用时需要手动释放锁,使用起来更加的灵活,但是也容易遗漏unlock
  • Sychronized在申请锁失败时,会死等知道获取到锁;ReentrantLock可以通过trylock的方式等待一段时间之后就放弃等待
  • Synchronized是非公平锁;ReentrantLock默认是非公平锁,可以通过构造方法传入一个true开启公平锁模式
  • 更强大的唤醒机制 . synchronized 是通过 Object wait / notify 实现等待 - 唤醒 . 每次唤醒的是一个随机等待的线程. ReentrantLock 搭配 Condition 类实现等待 - 唤醒 , 可以更精确控制唤醒某个指定的线程.
  • ReentrantLock提供了一种能够中断等待锁的线程的机制,通过 lock.lockInterruptibly() 来实现这个机制。也就是说正在等待的线程可以选择放弃等待,改为处理其他事情。

5.可中断锁和不可中断锁有什么区别

  • 可中断锁:获取锁的过程中可以被中断,不需要一直等到获取锁之后 才能进行其他逻辑处理。ReentrantLock 就属于是可中断锁。
  • 不可中断锁:一旦线程申请了锁,就只能等到拿到锁以后才能进行其他的逻辑处理。 synchronized 就属于是不可中断锁

6.如何选择使用哪个锁?

  • 锁竞争不激烈的时候, 使用 synchronized, 效率更高, 自动释放更方便.
  • 锁竞争激烈的时候, 使用 ReentrantLock, 搭配 trylock 更灵活控制加锁的行为, 而不是死等.
  • 如果需要使用公平锁, 使用 ReentrantLock.

二,Atomic原子类

1.什么是原子类

Atomic 翻译成中文是原子的意思。在化学上,我们知道原子是构成一般物质的最小单位,在化学反应中是不可分割的。在我们这里 Atomic 是指一个操作是不可中断的。即使是在多个线程一起执行的时候,一个操作一旦开始,就不会被其他线程干扰。

所以,所谓原子类就是具有原子操作特征的类

根据操作的数据类型,可以将JUC包中的原子类分为四类:

  1. 基本类型
    1. AtomicInteger:整型原子类
    2. AtomicLong:长整型原子类
    3. AtomicBoolean:布尔型原子类
  2. 数组类型
    1. AtomicIntegerArray:整型数组原子类
    2. AtomicLongArray:长整型数组原子类
    3. AtomicReferenceArray:引用类型数组原子类
  3. 引用类型
    1. AtomicReference:引用类型原子类
    2. AtomicMarkableReference:原子更新带有标记的引用类型。该类将 boolean 标记与引用关联起来。
    3. AtomicStampedReference:原子更新带有版本号的引用类型。该类将整数值与引用关联起来,可用于解决原子的更新数据和数据的版本号,可以解决使用 CAS 进行原子更新时可能出现的 ABA 问题。
  4. 对象的属性修改类型
    1. AtomicIntegerFieldUpdater:原子更新整型字段的更新器
    2. AtomicLongFieldUpdater:原子更新长整型字段的更新器
    3. AtomicReferenceFieldUpdater:原子更新引用类型里的字段

2.基本类型原子类

以AtomicInteger为例:常用方法有

public final int get() //获取当前的值
public final int getAndSet(int newValue)//获取当前的值,并设置新的值
public final int getAndIncrement()//获取当前的值,并自增
public final int getAndDecrement() //获取当前的值,并自减
public final int getAndAdd(int delta) //获取当前的值,并加上预期的值
boolean compareAndSet(int expect, int update) //如果输入的数值等于预期值,则以原子方式将该值设置为输入值(update)
public final void lazySet(int newValue)//最终设置为newValue,使用 lazySet 设置之后可能导致其他线程在之后的一小段时间内还是可以读到旧的值。

3.基本数据类型优势

通过一个简单例子带大家看一下基本数据类型原子类的优势

1、多线程环境不使用原子类保证线程安全(基本数据类型)

class Test {private volatile int count = 0;//若要线程安全执行执行count++,需要加锁public synchronized void increment() {count++;}public int getCount() {return count;}
}

 2、多线程环境使用原子类保证线程安全(基本数据类型)

class Test2 {private AtomicInteger count = new AtomicInteger();public void increment() {count.incrementAndGet();}//使用AtomicInteger之后,不需要加锁,也可以实现线程安全。public int getCount() {return count.get();}
}

AtomicInteger 类主要利用 CAS (compare and swap) + volatile 和 native 方法来保证原子操作,从而避免 synchronized 的高开销,执行效率大为提升。

三,CSA

1.什么是CAS?

CAS即compare and swap(比较与交换),它的主要思想很简单:就是用一个预期值和一个要更新变量的值进行比较,两值相等才会将新的值写入,否则不会操作成功。

CAS 是一个原子操作,底层依赖于一条 CPU 的原子指令实际上Java的CAS利用的是unsafe这个类提供的CAS操作;unsafe的CAS依赖于JVM针对于不同的操作系统实现Atomic::cmpxchg;Atomic::cmpxchg的实现使用了汇编语言的CAS操作,并使用CPU提供的lock机制保证其原子性。简而言之,有了硬件层面的支持,软件层面才可以做到

2.CAS操作

CAS涉及三个操作数:

  • V:要更新的变量
  • E:预期值
  • N:拟写入的新值

当且仅当V的值等于E的值时,CAS通过原子方式用新值N来更新V的值,如果不等,说明已经有其他线程更新了V,则当前线程放弃更新。

3.CAS的ABA问题

如果一个变量 V 初次读取的时候是 A 值,并且在准备赋值的时候检查到它仍然是 A 值,那我们就能说明它的值没有被其他线程修改过了吗?很明显是不能的,因为在这段时间它的值可能被改为其他值,然后又改回 A,那 CAS 操作就会误认为它从来没有被修改过。这个问题被称为 CAS 操作的 "ABA"问题。

对于这个问题,我们解决的方法是:给要修改的值,引入版本号,在CAS比较当前值和预期值相同的同时也需要比较版本号是否符合预期

  • CAS 操作在读取旧值的同时, 也要读取版本号.
  • 真正修改的时候,
    • 如果当前版本号和读到的版本号相同, 则修改数据, 并把版本号 + 1.
    • 如果当前版本号高于读到的版本号. 就操作失败(认为数据已经被修改过了)

四,AQS

1.AQS是什么?

AQS 的全称为 AbstractQueuedSynchronizer ,翻译过来的意思就是抽象队列同步器。这个类在 java.util.concurrent.locks 包下面。AQS就是一个抽象类,主要用来构建锁和同步器。AQS 为构建锁和同步器提供了一些通用功能的实现,因此,使用 AQS 能简单且高效地构造出应用广泛的大量的同步器,比如我们提到的ReentrantLock,Semaphore等

2.AQS的原理是什么

AQS的核心思想就是:如果被请求的共享资源空闲,则将当前请求资源的线程设置为有效的工作线程,并将共享资源设置为锁定状态;如果被请求的共享资源被占用,那么就需要一套线程阻塞等待以及被唤醒时锁分配的机制,这个机制AQS使用CLH锁队列实现的,即将暂时获取不到锁的线程加载到队列当中。

CLH队列是一个虚拟的双向队列(虚拟的双向队列即不存在队列实例,仅存在节点之间的关联关系)。AQS是将每一个请求共享资源的线程封装成一个CLH锁队列的一个结点来实现锁的分配。在CLH同步队列中,一个结点表示一个线程,它保存着线程的引用,当前结点在队列中的状态,前驱结点,后继结点。

AQS的核心原理图: 

AQS使用int成员变量state表示同步状态 ,通过内置的线程等待队列来完成获取资源线程的排队工作。state变量由volatile修饰,用于展示当前临界资源获锁情况。

// 共享变量,使用volatile修饰保证线程可见性
private volatile int state;

以ReentrantLock为例,state初始值为0,表示资源未锁定状态 。此时线程A调用lock()时,底层会调用tryAcquire()方法独占该锁并将state + 1,此后,其他线程想要再次tryAcquire()时都会失败,直到A线程unlock()之后,state = 0,其他线程才有机会获取该锁。ReentrantLock是一个可重入锁,加锁多少次就需要解锁多少次,直到state = 0,此时证明资源无锁状态,其他线程均可获取。

CountDownLatch 以例,任务分为 N 个子线程去执行,state 也初始化为 N(注意 N 要与线程个数一致)。这 N 个子线程是并行执行的,每个子线程执行完后countDown() 一次,state 会 CAS(Compare and Swap) 减 1。等到所有子线程都执行完后(即 state=0 ),会 unpark() 主调用线程,然后主调用线程就会从 await() 函数返回,继续后余动作

3.Semaphore 有什么用?

synchronized 和 ReentrantLock 都是一次只允许一个线程访问某个资源,而Semaphore(信号量)可以用来控制同时访问特定资源的线程数量

Semaphore简单使用:在某时刻同时存在N(N > n)个线程来获取Semaphore中的共享资源,但是Semaphore在创建实例对象时传入参数来设置同一时刻只能有n个对象访问,其他线程都会阻塞等待,直到有线程释放资源之后,其他线程才可以获取,但是只要同时访问线程数量为n,其他线程就会阻塞

 例:

// 初始共享资源数量
final Semaphore semaphore = new Semaphore(5);
// 获取1个许可
semaphore.acquire();
// 释放1个许可
semaphore.release();

此时只允许同时5个线程访问。用我们生活中的例子来讲:一个加油站同时只能有5辆车加油,其他车辆来到只能等待,只有其他车辆完成加油之后,这个位置空闲,后续车辆才能加油!

当初始资源数为1的时候,Semaphore为排他锁!

Semaphore有两种模式:

  • 公平模式:调用acquire()方法的顺序就是获取许可证的顺序,遵循FIFO,
  • 非公平模式:抢占式执行

两种模式对应两个构造方法: 两个构造方法必须提供许可证数量,第二个方法可以指定是否为公平模式,默认是非公平模式

public Semaphore(int permits) {sync = new NonfairSync(permits);
}public Semaphore(int permits, boolean fair) {sync = fair ? new FairSync(permits) : new NonfairSync(permits);
}

4.Semaphore的原理是什么

Semaphore是共享锁的一种实现,它默认构造AQS的state为permits,可以将permits的值看作许可证的数量,只有拿到许可证的线程才能执行。

调用Semaphore.acquire(),线程尝试获取许可证,如果state >= 0的话,则表示获取成功,如果获取成功,则进程CAS操作去修改state的值 state = state - 1;如果state  < 0 时,则表示许可证数量不足,此时将线程封装成一个结点加入到阻塞队列当中,挂起线程。

调用Semaphore.release(); ,线程尝试释放许可证,并使用 CAS 操作去修改 state 的值 state=state+1。释放许可证成功之后,同时会唤醒同步队列中的一个线程。被唤醒的线程会重新尝试去修改 state 的值 state=state-1 ,如果 state>=0 则获取令牌成功,否则重新进入阻塞队列,挂起线程

5. CountDownLatch有什么用?

CountDownLatch允许count个线程阻塞在一个地方,直至所有线程的任务执行完毕才结束

CountDownLatch是一次性的,计数器的值只能在构造方法中初始化一次,之后没有任何机制再对其设置值,当CountDownLatch使用完毕之后,不能再被使用。

6.CountDownLatch 的原理是什么?

CountDownLatch是一种共享锁的实现,它默认构造AQS的state值作为count,当线程使用countDown()方法时,其实使用了tryReleaseShared以CAS的操作来减少state的值直至为0,当调用await()方法时,如果state不等于0,证明还有线程没有执行完毕任务,await()方法一值会阻塞,也就是说await()方法之后的语句不会被执行直到count的个数位0,也就是state = 0,await()方法之后的语句才执行。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/45871.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

不同企业如何选择合适的CRM系统?

市场上的CRM系统千差万别&#xff0c;如何选到适合的CRM系统&#xff1f;很多企业凭借感觉盲目选型&#xff0c;结果上线后发现CRM系统功能不符合需求。这就好比买衣服&#xff0c;不试穿就买回家&#xff0c;结果发现尺码不合适&#xff0c;还不能退换。下面说说企业如何进行C…

series的数据对齐功能

Series 是一种类似于 Numpy 中一维数组的对象&#xff0c;它由一组任意类型的数据以及一组与之相关的数据标签&#xff08;即索引&#xff09;组成。举个最简单的例子&#xff1a; 上面的代码将打印出如下内容&#xff1a; 左边的是数据的标签&#xff0c;默认从 0 开始依次递增…

Git 目录详解

一、Git目录详解 在使用Git时&#xff0c;有几个目录和文件在Git项目中扮演着重要的角色&#xff0c;下面详细介绍一下这些目录和文件的作用 1、.git目录 .git目录是Git项目的核心&#xff0c;包含了Git的版本库和元数据等重要信息。在该目录中&#xff0c;有一些重要的子目录和…

嵌入式:ARM Day6

作业:完成cortex-A7核UART总线实验 目的&#xff1a;1.输入a,显示b&#xff0c;将输入的字符的ASCII码下一位字符输出 2.原样输出输入的字符串 源码&#xff1a; uart4.h #ifndef __UART4_H__ #define __UART4_H__#include "stm32mp1xx_rcc.h" #incl…

excel条件格式:不同组对应位置对比标记

问题描述 下图中有两组数据&#xff0c;想要对比两个对应位置的数据并标记 条件格式 选中其中一个单元格&#xff0c;条件格式->新建规则 使用公式确定要设置格式的单元格&#xff0c;自定义需求 格式化剩余同样标准的单元格

QT中资源文件resourcefile的使用

QT中资源文件resourcefile的使用 之前添加图标的方法使用资源文件的方法创建资源文件资源文件添加前缀资源文件添加资源使用资源文件中的资源 在Qt中引入资源文件好处在于他能提高应用程序的部署效率并且减少一些错误的发生。 在程序编译过程中&#xff0c; 添加到资源文件中的…

安全学习DAY17_信息打点-语言框架组件识别

信息打点-WEB打点-语言框架&开发组件 文章目录 信息打点-WEB打点-语言框架&开发组件本节涉及链接&工具本节知识&思维导图基础概念介绍框架&#xff1a;组件&#xff1a;Web架构 对应Web测试手法后端&#xff1a;前端组件&#xff1a;java居多&#xff0c;框架&…

广州华锐互动:3D数字孪生开发编辑器助力企业高效开发数字孪生应用

3D数字孪生开发编辑器是一种新兴的技术&#xff0c;它可以帮助企业更好地管理和维护其物联网设备。这些工具可以帮助企业实现对设备的实时监控、故障排除和优化&#xff0c;从而提高生产效率和降低成本。 数字孪生系统是一种将物理世界与数字世界相结合的技术&#xff0c;它可以…

点亮一颗LED灯

TOC LED0 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);//使能APB2的外设时钟GPIO_InitTypeDef GPIO_Initstructure;GPIO_Initstructure.GPIO_Mode GPIO_Mode_Out_PP;//通用推挽输出GPIO_Initstructure.GPIO_Pin GPIO_Pin_5;GPIO_Initstructure.GPIO_Speed GPIO_S…

Midjourney API 申请及使用

在人工智能绘图领域&#xff0c;想必大家听说过 Midjourney 的大名吧&#xff01; Midjourney 以其出色的绘图能力在业界独树一帜。无需过多复杂的操作&#xff0c;只要简单输入绘图指令&#xff0c;这个神奇的工具就能在瞬间为我们呈现出对应的图像。无论是任何物体还是任何风…

Java请求Http接口-OkHttp(超详细-附带工具类)

简介&#xff1a;OkHttp是一个默认有效的HTTP客户端&#xff0c;有效地执行HTTP可以加快您的负载并节省带宽&#xff0c;如果您的服务有多个IP地址&#xff0c;如果第一次连接失败&#xff0c;OkHttp将尝试备用地址。这对于IPv4 IPv6和冗余数据中心中托管的服务是必需的。OkHt…

LVS 负载均衡集群

集群 集群&#xff08;Cluster&#xff09;是一组相互连接的计算机或服务器&#xff0c;它们通过网络一起工作以完成共同的任务或提供服务。集群的目标是通过将多台计算机协同工作&#xff0c;提高计算能力、可用性、性能和可伸缩性&#xff0c;适用于大量高并发的场景。 集群…

【HBZ分享】java中的BitSet 与 Redis中的BitMap 与 布隆过滤器

BitMap的存储原理 bitMap他会标识出某个整数是否存在&#xff0c;存在即为1&#xff0c;不存在对应位即为0bitMap是存储int类型的&#xff0c;int 4byte&#xff0c; 1byte 8bit&#xff0c;因此bitMap数组中的每个下标可以标识出32个数字是否存在bitMap相当于一个个小格子&…

马哈鱼数据血缘工具背后的项目: gsp_demo_java 项目简单介绍与使用

0.背景 马哈鱼数据血缘工具(https://www.sqlflow.cn/)是SQLflow工具的中文译名,实际就是sqlflow. 对于SQL flow来说,底层调用的是General SQL Parser(GSP https://sqlparser.com) 的库. 这个gsp有开源的java demo项目:https://github.com/sqlparser/gsp_demo_java 1.快速使用…

第6章:支持向量机

间隔与支持向量 w为法向量&#xff0c;决定的是超平面的方向。b是偏移项&#xff0c;决定了超平面与原点之间的距离。 为什么最大化间隔&#xff0c;得到的就是最优平面呢&#xff1f; 当超平面没有正确划分正负样本时&#xff0c;几何间隔为负数。几何间隔&#xff0c;各个…

网络编程基础(1)

目录 网络编程解决是跨主机的进程间通讯 1、网络 2、互联网 3、ip地址 &#xff08;1&#xff09;ipv4: &#xff08;2&#xff09;ipV6:1 &#xff08;3&#xff09;IP地址的组成&#xff1a; (4)Linux查看IP地址&#xff1a;ifconfig 4、mac地址 5、ping Ip地址 6…

VisualVM(All-in-One Java Troubleshooting Tool)多合-故障处理工具

VisualVM&#xff1a;多合-故障处理工具 VisualVM&#xff08;All-in-One Java Troubleshooting Tool&#xff09;是功能最强大的 运行监视 和 故障处理 程序之一&#xff0c;曾经在很长一段时间内是Oracle官方主力发展的虚拟机故障处理工具。Oracle曾在VisualVM的软件说明中写…

数据结构--最短路径 Floyd算法

数据结构–最短路径 Floyd算法 F l o y d 算法&#xff1a;求出每⼀对顶点之间的最短路径 \color{red}Floyd算法&#xff1a;求出每⼀对顶点之间的最短路径 Floyd算法&#xff1a;求出每⼀对顶点之间的最短路径 使⽤动态规划思想&#xff0c;将问题的求解分为多个阶段 对于n个顶…

QT学习笔记-Linux ARM环境下实现QT程序通过ODBC驱动访问SQLServer数据库

QT学习笔记-Linux ARM环境下实现QT程序通过ODBC驱动访问SQLServer数据库 0、背景1、基本环境2、搭建交叉编译环境3、在交叉编译服务器上交叉编译安装unixODBC3.1 下载unixODBC3.2 交叉编译unixODBC3.2.1 基本编译说明3.2.2 交叉编译说明3.2.3 ./configure -build,-host,-target…

不知道开黑语音哪个软件好?

黑盒语音官方网站&#xff1a;https://chat.top 免费支持AI降噪免费支持高品质立体声免费支持码率128Kbps,192Kbps免费支持上传100M文件免费支持动态房间头像和横幅支持更多自定义动态表情即将支持更多免费功能