乐观锁,悲观锁定义以及常见类型

什么是乐观锁和悲观锁?

悲观锁(Pessimistic Locking):具有强烈的独占和排他特性,悲观锁就是“总有刁民想害朕”的具象化,它指的是对数据被外界修改持保守态度。因此,在整个执行过程中,将处于锁定状态。所以,悲观锁是一种悲观思想,它总认为最坏的情况可能会出现,它认为数据很可能会被其他人所修改,所以悲观锁在持有数据的时候总会把资源 或者 数据 锁住,这样其他线程想要请求这个资源的时候就会阻塞,直到等到悲观锁把资源释放为止。Java 中的Synchronized 和 ReentrantLock 是一种悲观锁思想的实现,因为 Synchronzied ReetrantLock 不管是否持有资源,它都会尝试去加锁。
乐观锁(Optimistic Locking) :相对悲观锁而言,就是“人之初,性本善”,乐观锁机制采取了更加宽松的加锁机制。乐观锁的思想与悲观锁的思想相反,它总认为资源和数据不会被别人所修改,所以读取不会上锁,不过倒也没有那么傻,不至于“骑车不锁车,睡觉不关门”它在进行写入操作的时候会判断当前数据是否被修改过。Java中的StampedLockAtomicInteger 是一种乐观锁思想的实现。

ReadWriteLock

ReentrantLock保证了只有一个线程可以执行临界区代码,在资源被一个线程占用时,其余线程只能等待,我们会发现这种保护太过头了,我们更想要的是,如果只是读取操作是可以多个线程同时调用的,但是当一旦有写入操作时,其他线程就必须等待了。使用ReadWriteLock就可以解决这个问题。它保证:

        1.只允许一个线程写入(其他线程既不能写入也不能读取)
        2.没有写入时,多个线程允许同时读(提高性能)

用 ReadwriteLock 实现这个功能十分容易。我们需要创建一个 ReadriteLock 实例,然后分别获
取读锁和写锁:

public class counter {private final ReadwriteLock rwlock = new ReentrantReadwriteLock();private final Lock rlock = rwlock.readLock();private final Lock wlock = rwlock.writeLock();private int[] counts = new int[10];public void inc(int index) {wlock.lock();// 加写锁try {counts[index] += 1;} finally {wlock.unlock();//释放写锁}}public int[] get() {rlock.lock();// 加读锁try {return Arrays.copy0f(counts, counts.length);} finally {rlock.unlock();//释放读锁}}
}

把读写操作分别用读锁和写锁来加锁,在读取时,多个线程可以同时获得读锁,这样就大大提高
了并发读的执行效率。
如果我们深入分析 ReadwriteLock,会发现它有个潜在的问题:如果有线程正在读,写线程需要等待读线程释放锁后才能获取写锁,即读的过程中不允许写。

StampedLock

Java 8 进一步提升并发执行效率,引入了新的读写锁:stampedLock
stampedLock 和 ReadwriteLock 相比,改进之处在于:读的过程中也允许获取写锁写入!这样一来,我们读的数据就可能不一致,需要一点额外的代码来判断读的过程中是否有写入。所以,这种读锁是一种乐观锁。

class point {private final StampedLock stampedLock = new StampedLock();private double x;private double y;public void move(double deltaX, double deltaY) {long stamp = stampedLock.writeLock();try {x += deltaX;y += deltaY;} finally {stampedLock.unlockWrite(stamp);}}public double distanceFromOrgigin() {//注意下面两行代码不是原子操作//假设x,y=(100,200)double currentX = x;//此处已经读取x=100,但x,y可能被改写线程修改为(300,400)double currentY = y;//此处读取到y,如果未改写,读取到的为(100,200)//如果被改写,读取到的为(100,400)long stamp = stampedLock.readLock();//检查乐观锁的版本号是否一致if (!stampedLock.validate(stamp)) {//获取读锁(悲观锁)stamp = stampedLock.tryReadLock();//重新读取try {currentX = x;currentY = y;} finally {stampedLock.unlockRead(stamp);}}return Math.sqrt(currentX * currentX + currentY * currentY);}}

和 ReadriteLock 相比,写入的加锁是完全一样的,不同的是读取。

首先我们通过 tryOptimisticRead()获取一个乐观读锁,并返回版本号。

接着进行读取,读取完成后,我们通过 validate()去验证版本号,如果在读取过程中没有写入,版本号不变,验证成功,我们就可以放心地继续后续操作。

如果在读取过程中有写入,版本号会发生变化,验证将失败。在失败的时候,我们再通过readLock()获取悲观读锁再次读取。由于写入的概率不高,程序在绝大部分情况下可以通过乐观读锁获取数据,极少数情况下使用悲观读锁获取数据。
        所以, stampedLock 把读锁细分为乐观读和悲观读,能进一步提升并发效率。但这也是有代价的:

一是代码更加复杂,二是 stampedLock 是不可重入锁,不能在一个线程中反复获取同一个锁。

Semaphore

通过各种锁的实现,我们会发现锁的目的是保护一种受限资源,保证同一时刻只有一个线程能访问(ReentrantLock),或者只有一个线程能写入(ReadWriteLock),
还有一种受限资源,它需要保证同一时刻最多有 N个线程能访问,比如同一时刻最多创建 100 个数据库连接,最多允许 10 个用户下载等。
这种限制数量的锁,可以用 Lock 数组来实现,但是很麻烦。类似需求常见更适合 Semaphore信号量。Semaphore 本质上就是一个信号计数器,用于限制同一时间的最大访问数量。
例如,最多允许3个线程同时访问:

public class AccessLimitcontrol {// 任意时刻仅允许最多3个线程获取许可:final Semaphore semaphore = new Semaphore(3);public string access() throws Exception {
// 如果超过了许可数量,其他线程将在此等待:semaphore.acquire();try {
// TODO:return UuID.randomUUID().tostring();} finally {semaphore.release();}}
}

使用Semaphore先调用acquire()方法,然后通过try...finally保证在finally中释放锁。

调用acquire()方法可能会进入等待,直到满意为止。也可以使用tryAcquire()指定等待时间:

    if(semaphore.tryAcquire(3,TimeUnit.SECONDS){//指定等待时间3秒内获取到许可try{//TODO}finally {semaphore.release();}}

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

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

C++的哲学思想

C的哲学思想 文章目录 C的哲学思想💡前言💡C的哲学思想☁️C底层不应该基于任何其他语言(汇编语言除外)☁️只为使用的东西付费(不需要为没有使用到的语言特性付费)☁️以低成本提供高级抽象(更…

WebAssembly进阶,vue3 使用 WebAssembly,及 WebAssembly vs JavaScript 的性能对比

目录 核心使用步骤 .c文件.cpp文件编译 使用 Emscripten 转译文件 页面中引入.wasm文件中的函数 WebAssembly vs JavaScript 的性能对比 性能对比关键点: 具体场景 实际案例分析 如果对WebAssembly不熟悉可以前往:WebAssembly最详教程,进行WebAssembly基础学习 Web…

【有啥问啥】Stackelberg博弈方法:概念、原理及其在AI中的应用

Stackelberg博弈方法:概念、原理及其在AI中的应用 1. 什么是Stackelberg博弈? Stackelberg博弈(Stackelberg Competition)是一种不对称的领导者-追随者(Leader-Follower)博弈模型,由德国经济学…

【LVIO-SLAM】 激光slam理论与实践

【LVIO-SLAM】 激光slam理论与实践 1. 激光slam理论与实践1.1 2D激光SLAM1.2 3D激光SLAM 2. 激光雷达运动畸变去除2.1 Lidar数学模型与点云去畸变2.2 运动畸变 3. 激光slam的前端配准3.1 帧间匹配3.2 3.2 ICP (Iterative Closest Point)3.3 PI-ICP (Point-to-Line Iterative Cl…

ubuntu22 解决docker无法下载镜像问题

参考在 Ubuntu 中安装 Docker_ubuntu安装docker-CSDN博客 安装docker完成后,运行如下命令验证 Docker 服务是否在运行: systemctl status docker 运行(sudo docker run hello-world)例子报错: 问题:Docker…

Vue 内存泄漏分析:如何避免开发过程中导致的内存泄漏问题

一. 引言 Vue 作为一款流行的前端框架,已经在许多项目中得到广泛应用。然而,随着我们在 Vue 中构建更大规模的应用程序,我们可能会遇到一个严重的问题,那就是内存泄漏。内存泄漏是指应用程序在使用内存资源时未正确释放&#xff…

iPhone 16 还剩一个月,微软开源新技术让手机以 6 倍速度提前跑上大模型

作者 | 微软亚洲研究院 责编 | 王启隆 出品 | AI 科技大本营(ID:rgznai100) 随着人工智能技术的飞速发展,将大语言模型(LLMs)部署到边缘设备上已成为当前 AI 领域的一个热门趋势。这一趋势不仅体现在微软 Windows 11 AI PC 等产品…

【Qualcomm】高通SNPE框架简介、下载与使用

说明:基础内容!不建议订阅!不建议订阅!不建议订阅! 目录 一 高通SNPE框架 1 SNPE简介 2 QNN与SNPE 3 Capabilities 4 工作流程 二 SNPE的安装与使用 1 下载 2 Setup 3 SNPE的使用概述 一 高通SNPE框架 1 SNP…

Axure精选各类组件案例集锦:设计灵感与实战技巧

在设计大屏页面时,设计师们面临着如何构建丰富、直观且用户友好的界面的挑战。幸运的是,Axure等强大的原型设计工具提供了丰富的可视化组件库,为设计师们提供了无限的设计灵感和实战技巧。本文将通过精选的各类组件案例,探讨大屏设…

综合题第二题(路由器的配置)

题目 如何计算子网掩码 我们可以观察到上图的IP地址后面有“/26”、“30”。我们都知道子网掩码是由多个连续“1”和多个连续“0”组成的,“、26”表示子网掩码的二进制表达中有26个1。 例如:156.95.9.128/26 1111 1111.1111 1111.1111 1111.1100 0000…

摒弃“流量思维”,以精准流量驱动企业发展——基于开源 AI 智能名片、链动 2+1 模式及 O2O 商城小程序的思考

摘要:本文深入探讨在当前竞争激烈的营销环境下,摒弃“流量思维”的紧迫性与必要性。强调做内容营销不能仅仅局限于发文案,而应摆脱一味追求阅读量、推荐量和粉丝数的误区,聚焦于获取精准流量。结合开源 AI 智能名片、链动 21 模式…

??实验——完全使用Ansible部署多台服务器的服务

文章目录 需求两台Web服务器部署同一Web应用WeCenter,且两台服务器的用户上传的数据目录挂载到共享存储服务器中,总数据保存在一台数据库服务器中使用sersync简单实现两台共享存储服务器之间的Web应用共享数据目录的数据同步每天定时将两台Web服务器的We…

中国中车在线测评考的啥?大易题库如何通过|附真题型国企题库通关秘籍和攻略

言语理解题目:这类题目主要考察你的语言理解和表达能力,例如,给你一个段落,让你根据段落内容选择最合适的答案。要点是快速捕捉文段中的关键信息,理解作者的意图和观点 逻辑推理题目:这类题目需要你从一组…

盘点那些功能强大的思维导图在线工具,你用过几个

如果我们日常遇到比较繁杂的信息需要梳理,那我比较推荐使用思维导图在线工具进行梳理。这些工具可以通过图形化的方式展示各种信息之间的关系。这篇文章我将要介绍几款好用的思维导图工具帮我们更好的组织思维。 1.福晰思维导图 链接一下:https://www.…

RAG技术全面解析:Langchain4j如何实现智能问答的跨越式进化?

LLM 的知识仅限于其训练数据。如希望使 LLM 了解特定领域的知识或专有数据,可: 使用本节介绍的 RAG使用你的数据对 LLM 进行微调结合使用 RAG 和微调 1 啥是 RAG? RAG 是一种在将提示词发送给 LLM 之前,从你的数据中找到并注入…

记录:ubuntu20.04的安装和必要的开发准备

记录ubuntu20.04的安装和必要的开发准备 准备1. 安装ubuntu20.04时的Tips2. 屏幕亮度调节问题3. 解决 "No Wi-Fi Adapter Found"4. Nvidia Driver && cuda5. 修改安装源6. ssh 远程开发 准备 没有装双系统,只有 ubuntu20.04,记录安装之…

微服务--Gateway网关

在微服务架构中,Gateway(网关)是一个至关重要的组件,它扮演着多种关键角色,包括路由、负载均衡、安全控制、监控和日志记录等。 Gateway网关的作用 统一访问入口: Gateway作为微服务的统一入口&#xff0c…

HTTP协议1.1请求头和keep-alive

请求头分类 End-to-end(端对端) 必须全部带给目标服务器,不会被中途变化或去掉 Hop-by-hop(逐跳头) 比如客户端发请求,要路过代理(例如Nginx),头可以被自动删掉,来到真正服务器上…

IAR创建工程与工程配置

第一步:先创建一个新的工作区间 第二步:创建一个新的工程(工程名与文件夹名字要一致) 第三步:添加组 第四步:往各个组里添加文件 第五步:配置工程 因为我的程序下载是通过ST-link的SWD&#xf…

正向科技|格雷母线定位系统的设备接线安装示范

格雷母线安装规范又来了,这次是设备接线步骤 格雷母线是格雷母线定位系统的核心部件,沿着移动机车轨道方向上铺设,格雷母线以相互靠近的扁平状电缆与天线箱电磁偶合来进行信号传递,从而检测得到天线箱在格雷母线长度方向上的位置。…