您当前的位置:首页 > 电脑百科 > 程序开发 > 算法

使用乐观锁优化并行操作

时间:2022-08-28 12:11:19  来源:网易  作者:互联网资讯看板

我们知道悲观锁在高并发的场景下,激烈的锁竞争会造成线程阻塞,大量阻塞线程会导致系统上下文切换,增加系统的性能开销。那么有没有可能实现一种非阻塞的锁机制来保证线程的安全呢?答案是肯定的。今天我就带你学习下乐观锁的优化方法,看看怎么使用才能发挥它最大的价值。

一 什么是乐观锁

乐观锁,顾名思义,就是说在操作共享资源时,它总是抱着乐观的态度进行,它认为自己可以成功的完成操作。但实际上,当多个线程同时操作一个共享资源时,只有一个线程会成功,那么失败的线程呢?它们不会像悲观锁一样,在操作系统中挂起,而仅仅是返回,并且系统允许失败的线程重试,也允许自动放弃退出操作。

所以,乐观锁相比悲观锁来说,不会带来死锁,饥饿等活性故障问题,线程间的相互影响也远远比悲观锁要小。更为重要的是,乐观锁没有因锁竞争造成的系统开销,所以在性能上也是更胜一筹。

二 乐观锁的实现原理

CAS是实现乐观锁的核心算法,它包含了3个参数:V(需要更新的变量),E(预期值)和N(最新值)。

1.CAS如何实现原子操作

在JDK中的concurrent包中,atomic路径下的类都是基于CAS实现的。AtomicInteger就是基于CAS实现的一个线程安全的整型类。下面我们通过源码来了解下如何使用CAS实现原子操作。

我们可以看到AtomicInteger的自增方法是使用了Unsafe的getAndAddInt方法,显然AtomicInteger依赖于本地方法Unsafe类,Unsafe类中的操作方法会调用CPU底层指令实现原子操作。


 

2.处理器如何实现原子操作

CAS是调用处理器底层指令来实现原子操作,那么处理器底层又是如何实现原子操作的呢?

处理器和物理内存之间的通信速度要远慢于处理器间的处理速度,所以处理器有自己的内部缓存。如下图所示,在执行操作时,频繁使用的内存数据会缓存在处理器的L1,L2和L3高速缓存中,以加快频繁读取的速度。


 

三 优化CAS乐观锁

虽然乐观锁在并发性能上要比悲观苏优越,但是在于写大于读的操作场景下,CAS失败的可能性会增大,如果不放弃此次CAS操作,就需要循环做CAS重试,这无疑会长时间地占用CPU。

JAVA1.7中,通过以下代码我们可以看到:AtomicInteger的getAndSet方法中使用了for循环不断重试CAS操作,如果长时间不成功,就会给CPU带来非常大的执行开销。到了Java8,for循环虽然被去掉了,但是我们反编译Unsafe类时就可以发现该循环其实是被封装在了Unsafe类中,CPU的执行开销依然存在。


 

JDK1.8中,Java提供了一个新的原子类LongAdder,LongAdder在高并发的场景下比AtomicInteger和AtomicLong的性能更好,代价就是会消耗更多的内存空间。

LongAdder内部由一个base变量和一个cell[]数组组成。当只有一个写线程,没有竞争的情况下,LongAdder会直接使用base变量作为原子操作变量,通过CAS操作修改变量;当有多个写线程竞争的情况下,除了占用base变量的一个写线程之外,其它各个线程会将修改的变量写入到自己的槽cell[]数组中,最终结果可通过公式计算得出:


 

四 总结

在日常开发中,使用乐观锁最常见的场景就是数据库的更新操作了。为了保证操作数据库的原子性,我们常常会为每一条数据定义一个版本号,并在更新前获取到它,到了更新数据库的时候,还要判断下已经获取的版本号是否被更新过,如果没有,则执行该操。

CAS乐观锁在平常使用时比较受限,它只能保证单个变量操作的原子性,当涉及到多个变量时,CAS就无能为力了,但前两讲讲到的悲观锁可以通过对整个代码块加锁来做到这点。

CAS乐观锁在高并发写大于读的场景下,大部分线程的原子操作会失败,失败后的线程将会不断重试CAS原子操作,这样就会导致大量线程长时间地占用CPU资源,给系统带来很大的性能开销。在JDK1.8中,Java新增了一个原子类LongAdder,它使用了空间换时间的方法,解决了上诉问题。

最近的这几讲中,我详细的讲解了基于JVM实现的同步锁Sychronized,AQS实现的同步锁Lock以及CAS实现的乐观锁。相信你也很好奇,这三种锁,到底哪一种的性能最好,现在我们来对比一下不同实现方式下的锁的性能。

鉴于脱离实际业务场景的性能对比测试结果没有意义,我们可以分别在“读多写少”,“读少写多”,“读写差不多”这三种场景下进行测试。又因为锁的性能还与竞争的激烈程度有关,所以除此之外,我们还将做三种锁在不同竞争级别下的性能测试。

综合上述条件,我将对四种模式下的五个锁Sychronized,ReentrantLock,ReentrantReadWriteLock,StampedLock以及乐观锁LongAdder进行压测。


 

通过以上结果,我们可以发现:在读大于写的场景下,读写锁ReentrantReadWriteLock,StampedLock以及乐观锁的读写性能是最好的;在写大于读的场景下,乐观锁的性能是最好的,其它4种锁的性能则差不多;在读和写差不多的场景下,两种读写锁以及乐观锁的性能要优于Sychronized和ReentrantLock。



Tags:乐观锁   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,不构成投资建议。投资者据此操作,风险自担。如有任何标注错误或版权侵犯请与我们联系,我们将及时更正、删除。
▌相关推荐
乐观锁算法:处理并发写操作的策略
在计算机科学领域,处理并发写操作一直是一个关键挑战。乐观锁算法是一种独特的方法,旨在处理这个问题。它的核心理念是基于一个假设:写操作的冲突是相对较少发生的。因此,它允许...【详细内容】
2023-10-18  Search: 乐观锁  点击:(141)  评论:(0)  加入收藏
提升并发性能的秘密武器:悲观锁与乐观锁解析
当谈到悲观锁和乐观锁时,我们通常是在讨论并发编程和多线程/多进程环境中的数据同步和访问控制问题。让我为您详细解释悲观锁和乐观锁的概念以及它们的使用。悲观锁(Pessimist...【详细内容】
2023-06-06  Search: 乐观锁  点击:(258)  评论:(0)  加入收藏
最全MySQL锁详解(含悲观+乐观锁+行锁+表锁+页锁等)
我们在操作数据库的时候,锁的冲突是影响数据库并发访问性能的一个非常重要因素,从这一角度来说,锁对于数据库而言就显得尤为重要。今天分享MySQL相关的最全锁@mikechen01MySQ...【详细内容】
2023-01-05  Search: 乐观锁  点击:(298)  评论:(0)  加入收藏
使用乐观锁优化并行操作
我们知道悲观锁在高并发的场景下,激烈的锁竞争会造成线程阻塞,大量阻塞线程会导致系统上下文切换,增加系统的性能开销。那么有没有可能实现一种非阻塞的锁机制来保证线程的安全...【详细内容】
2022-08-28  Search: 乐观锁  点击:(337)  评论:(0)  加入收藏
什么是悲观锁和乐观锁?
思维导图 文章已收录Github精选,欢迎Star:https://github.com/yehongzhi/learningSummary悲观锁悲观锁是平时开发中经常用到的一种锁,比如ReentrantLock和synchronized等就是这...【详细内容】
2020-12-01  Search: 乐观锁  点击:(305)  评论:(0)  加入收藏
Java性能 -- CAS乐观锁
synchronized / Lock / CAS synchronized和Lock实现的同步锁机制,都属于悲观锁,而CAS属于乐观锁 悲观锁在高并发的场景下,激烈的锁竞争会造成线程阻塞,而大量阻塞线程会导致系...【详细内容】
2019-09-18  Search: 乐观锁  点击:(1004)  评论:(0)  加入收藏
▌简易百科推荐
小红书、视频号、抖音流量算法解析,干货满满,值得一看!
咱们中国现在可不是一般的牛!网上的网友已经破了十个亿啦!到了这个互联网的新时代,谁有更多的人流量,谁就能赢得更多的掌声哦~抖音、小红书、、视频号,是很多品牌必争的流量洼地...【详细内容】
2024-02-23  二手车小胖说    Tags:流量算法   点击:(15)  评论:(0)  加入收藏
雪花算法详解与Java实现:分布式唯一ID生成原理
SnowFlake 算法,是 Twitter 开源的分布式 ID 生成算法。其核心思想就是:使用一个 64 bit 的 long 型的数字作为全局唯一 ID。在分布式系统中的应用十分广泛,且 ID 引入了时间戳...【详细内容】
2024-02-03   一安未来  微信公众号  Tags:雪花算法   点击:(51)  评论:(0)  加入收藏
程序开发中常用的十种算法,你用过几种?
当编写程序时,了解和使用不同的算法对解决问题至关重要。以下是C#中常用的10种算法,每个算法都伴随着示例代码和详细说明。1. 冒泡排序 (Bubble Sort):冒泡排序是一种简单的比...【详细内容】
2024-01-17  架构师老卢  今日头条  Tags:算法   点击:(45)  评论:(0)  加入收藏
百度推荐排序技术的思考与实践
本文将分享百度在推荐排序方面的思考与实践。在整个工业界的推广搜场景上,特征设计通常都是采用离散化的设计,需要保证两方面的效果,一方面是记忆,另一方面是泛化。特征都是通过...【详细内容】
2024-01-09  DataFunTalk  微信公众号  Tags:百度推荐   点击:(77)  评论:(0)  加入收藏
什么是布隆过滤器?如何实现布隆过滤器?
以下我们介绍了什么是布隆过滤器?它的使用场景和执行流程,以及在 Redis 中它的使用,那么问题来了,在日常开发中,也就是在 Java 开发中,我们又将如何操作布隆过滤器呢?布隆过滤器(Blo...【详细内容】
2024-01-05  Java中文社群  微信公众号  Tags:布隆过滤器   点击:(87)  评论:(0)  加入收藏
面向推荐系统的深度强化学习算法研究与应用
随着互联网的快速发展,推荐系统在各个领域中扮演着重要的角色。传统的推荐算法在面对大规模、复杂的数据时存在一定的局限性。为了解决这一问题,深度强化学习算法应运而生。本...【详细内容】
2024-01-04  数码小风向    Tags:算法   点击:(96)  评论:(0)  加入收藏
非负矩阵分解算法:从非负数据中提取主题、特征等信息
非负矩阵分解算法(Non-negativeMatrixFactorization,简称NMF)是一种常用的数据分析和特征提取方法,主要用于从非负数据中提取主题、特征等有意义的信息。本文将介绍非负矩阵分解...【详细内容】
2024-01-02  毛晓峰    Tags:算法   点击:(64)  评论:(0)  加入收藏
再谈前端算法,你这回明白了吗?
楔子 -- 青蛙跳台阶一只青蛙一次可以跳上一级台阶,也可以跳上二级台阶,求该青蛙跳上一个n级的台阶总共需要多少种跳法。分析: 当n=1的时候,①只需要跳一次即可;只有一种跳法,即f(...【详细内容】
2023-12-28  前端爱好者  微信公众号  Tags:前端算法   点击:(108)  评论:(0)  加入收藏
三分钟学习二分查找
二分查找是一种在有序数组中查找元素的算法,通过不断将搜索区域分成两半来实现。你可能在日常生活中已经不知不觉地使用了大脑里的二分查找。最常见的例子是在字典中查找一个...【详细内容】
2023-12-22  小技术君  微信公众号  Tags:二分查找   点击:(78)  评论:(0)  加入收藏
强化学习算法在资源调度与优化中的应用
随着云计算和大数据技术的快速发展,资源调度与优化成为了现代计算系统中的重要问题。传统的资源调度算法往往基于静态规则或启发式方法,无法适应动态变化的环境和复杂的任务需...【详细内容】
2023-12-14  职场小达人欢晓    Tags:算法   点击:(165)  评论:(0)  加入收藏
站内最新
站内热门
站内头条