【多线程 6】锁策略, cas 和 synchronized 优化过程


前言

之前分别详细的介绍过 synchronized 锁 和 锁策略,可以参考下边的链接

锁机制的“神锁“———synchronized [多线程系列4]-CSDN博客

历年必问高频面试题之——线程安全之常见的锁策略 ! ! !-CSDN博客

如果不了解 synchronized 锁 和 锁策略 的程序猿们,推荐先看看上边这两篇,会更好理解这里的内容。这里会介绍 java 官方的大佬们对 锁策略, cas 和 synchronized 进一步优化的详细过程。


一、锁策略 的优化

优化锁策略是多线程编程中重要的一环,它可以提高系统的并发性能和稳定性。以下是优化锁策略的一般过程:

  1. 分析并发访问模式: 了解并发访问模式是优化锁策略的第一步。通过分析哪些资源会被并发访问以及并发访问的频率和规模,可以确定哪些地方需要加锁以及何时加锁,选择哪种方式加锁。
  2. 选择合适的锁粒度: 锁粒度是指锁定的范围大小。通常情况下,锁的粒度越小,允许并发操作的程度就越高,但是管理多个锁也会增加开销。因此,需要根据具体情况选择合适的锁粒度,尽量减小锁的范围,提高并发性能。这里在上一篇 synchronized 锁 里提到过,对于锁的 粗化和细化。实际开发过程中,使⽤细粒度锁,是期望释放锁的时候其他线程能使⽤锁. 但是实际上可能并没有其他线程来抢占这个锁.这种情况JVM就会⾃动把锁粗化,避免频繁申请释放 锁.
  3. 使用读写锁(readers-writerlock): 如果资源的读操作远远多于写操作,可以考虑使用读写锁来提高并发性能。读写锁允许多个线程同时读取共享资源,但是只允许一个线程写入共享资源。
  4. 减少锁持有时间: 锁的持有时间越短,系统的并发性能就越高。因此,在使用锁的过程中,要尽量减少锁的持有时间,避免在锁的范围内进行耗时操作,可以将耗时操作放到锁的外部执行。
  5. 避免锁的嵌套: 锁的嵌套会增加代码的复杂性,容易导致死锁和性能问题。因此,尽量避免在一个锁的范围内获取其他锁,如果确实需要多个锁,可以尝试使用锁的顺序来避免死锁。
  6. 使用无锁数据结构: 对于高并发场景,可以考虑使用无锁数据结构,如CAS算法或者基于版本号的乐观锁。无锁数据结构能够减少锁的竞争,提高并发性能。
  7. 考虑并发容器: Java提供了一些并发容器,如ConcurrentHashMapConcurrentLinkedQueue等,它们内部使用了复杂的锁策略和数据结构,能够提供较好的并发性能。对于上述的 ConcurrentHashMap容器的详细介绍请参考我的这篇博客。大厂面试必考之——— HashTable, HashMap, ConcurrentHashMap !!【多线程 5】-CSDN博客

通过以上优化过程,可以有效提升系统的并发性能,降低线程竞争带来的性能损失,提高系统的稳定性和可靠性。


二、CAS 的介绍和优化

什么是CAS

CAS: 全称Compareandswap,字⾯意思:”⽐较并交换“,⼀个CAS涉及到以下操作:

我们假设内存中的原数据V,旧的预期值A,需要修改的新值B。

  1. ⽐较A与V是否相等。(⽐较)
  2. 如果⽐较相等,将B写⼊V。(交换)
  3. 返回操作是否成功

CAS是一种乐观锁的实现方式,它通过原子性的比较和交换操作来实现多线程并发的数据同步。CAS的优点是操作是原子性的,没有锁竞争,适用于对并发量较小的情况,性能较好。


CAS的应⽤

  1. 实现原⼦类 标准库中提供了 java.util.concurrent.atomic 包,⾥⾯的类都是基于这种⽅式来实现的. 典型的就是AtomicInteger类.其中的getAndIncrement相当于i++操作.
 AtomicInteger atomicInteger = new AtomicInteger(0);
 // 相当于 i++ 
atomicInteger.getAndIncrement();

示例:假设两个线程同时调⽤getAndIncrement

1. 两个线程都读取value的值到oldValue中.(oldValue是⼀个局部变量,在栈上.每个线程有⾃⼰的 栈)

2. 线程1先执⾏CAS操作.由于oldValue和value的值相同,直接进⾏对value赋值.

注意: • CAS是直接读写内存的,⽽不是操作寄存器.

• CAS的读内存,⽐较,写内存操作是⼀条硬件指令,是原⼦的.

3. 线程2再执⾏CAS操作,第⼀次CAS的时候发现oldValue和value不相等,不能进⾏赋值.因此需要 进⼊循环. 在循环⾥重新读取value的值赋给oldValue

4. 线程2接下来第⼆次执⾏CAS,此时oldValue和value相同,于是直接执⾏赋值操作.

5. 线程1和线程2返回各⾃的oldValue的值即可.

通过形如上述代码就可以实现⼀个原⼦类.不需要使⽤重量级锁,就可以⾼效的完成多线程的⾃增操作.

本来checkandset这样的操作在代码⻆度不是原⼦的.但是在硬件层⾯上可以让⼀条指令完成这个 操作,也就变成原⼦的了


实现⾃旋锁

基于CAS实现更灵活的锁,获取到更多的控制权.

⾃旋锁伪代码

 public class SpinLock {
 private Thread owner = null;
 public void lock(){
 // 通过 CAS 看当前锁是否被某个线程持有.  
 // 如果这个锁已经被别的线程持有, 那么就⾃旋等待.  
 // 如果这个锁没有被别的线程持有, 那么就把 owner 设为当前尝试加锁的线程.  
}
 }
 while(!CAS(this.owner, null, Thread.currentThread())){
 }
     public void unlock (){
     this.owner = null;
 }

CAS的ABA问题

CAS(比较并交换)操作在解决并发问题时可能会遇到ABA问题。ABA问题指的是在使用CAS进行原子操作时,可能会出现这样的情况:

  1. 初始时,一个线程读取共享变量的值为A。
  2. 后来,另一个线程将共享变量的值从A改为B。
  3. 然后,又有一个线程将共享变量的值从B改回A。如下图

在这种情况下,如果某个线程在CAS操作之前和之后读取共享变量的值,并且在CAS操作中没有检查变量的值是否发生过变化,那么这个线程可能会错误地认为CAS操作是成功的,因为变量的值仍然是A,尽管实际上它的值已经经历了从A到B再到A的变化。

这种情况可能导致程序出现意外的行为,因为有些操作可能会基于不正确的假设而执行,比如误以为共享变量的值从未被修改过。

为了解决ABA问题,可以采取以下几种方法:

  1. 版本号: 使用版本号来跟踪共享变量的变化历史。每次修改共享变量时,都将版本号递增,这样在进行CAS操作时除了比较变量的值外,还要比较版本号,以确保不会遇到ABA问题。 • 真正修改的时候                                                                                                                            ◦ 如果当前版本号和读到的版本号相同,则修改数据,并把版本号+1.                                            ◦ 如果当前版本号⾼于读 到的版本号.就操作失败(认为数据已经被修改过了).
  2. 引入额外的标记: 类似版本号,可以引入额外的标记位来跟踪变量的变化状态,从而避免ABA问题的发生。
  3. 使用带有ABA问题解决方案的CAS操作: 一些编程语言和库提供了带有ABA问题解决方案的CAS操作,比如Java中的AtomicStampedReferenceAtomicMarkableReference类。

通过以上方法,可以有效地解决或者减轻CAS操作中的ABA问题,确保程序在并发环境下的正确性。


相关⾯试题

1. 讲解下你⾃⼰理解的CAS机制

全称Compareandswap,即"⽐较并交换".相当于通过⼀个原⼦的操作,同时完成"读取内存,⽐较是 否相等,修改内存"这三个步骤.本质上需要CPU指令的⽀撑.

2. ABA问题怎么解决?

给要修改的数据引⼊版本号.

  1. 在CAS⽐较数据当前值和旧值的同时,也要⽐较版本号是否符合预期.
  2. 如 果发现当前版本号和之前读到的版本号⼀致,就真正执⾏修改操作,并让版本号⾃增;

最后

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数网络安全工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年网络安全全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。

img

img

img

img

img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上网络安全知识点!真正的体系化!

如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

20291)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上网络安全知识点!真正的体系化!

如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

  • 22
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: CASsynchronized都是用于实现多线程同步的机制,但它们的实现方式和应用场景有所不同。 CAS(Compare And Swap)是一种乐观机制,它通过比较内存中的值和期望值是否相等来判断是否需要更新,如果相等则更新,否则不更新。CAS的优点是无,可以避免线程的阻塞和唤醒,适用于并发量较大的情况。 synchronized是一种悲观机制,它通过获取来保证同一时刻只有一个线程可以执行临界区代码,其他线程需要等待的释放才能执行。synchronized的优点是可以保证线程安全,但缺点是会造成线程的阻塞和唤醒,适用于并发量较小的情况。 因此,CAS适用于并发量较大的情况,可以提高程序的性能;而synchronized适用于并发量较小的情况,可以保证程序的正确性。 ### 回答2: CAS(Compare and Swap)和synchronized都属于并发编程中用于实现线程安全的技术,但它们有一些重要的区别。 首先,CAS是一种乐观技术,而synchronized是一种悲观技术。CAS通过对比内存中的值与期望值,根据比较结果来决定是否更新内存中的值,无期间没有线程阻塞。相比之下,synchronized通过获取对象的来保证线程的同步执行,如果无法获取,线程会进入阻塞状态,直到获取到才能继续执行。 其次,CAS是基于底层硬件支持的原子操作指令来实现的,效率更高。而synchronized是基于Java的关键字实现的,需要涉及到用户态和内核态的切换,效率相对较低。 此外,CAS操作可以针对单个变量进行,并且只有在比较结果一致的情况下才会进行更新,适合对于多个线程并发更新同一个变量的情况。而synchronized可以用于对代码块或方法进行同步,可以保护多个线程访问共享资源的一致性。 最后,CAS相对于synchronized更容易产生ABA问题。ABA问题是指在CAS操作中,如果变量的值在操作过程中被其他线程修改过多次,并且最后又变回原来的值,那么CAS操作会错误地认为没有被修改过。为了解决ABA问题,Java提供了AtomicStampedReference和AtomicMarkableReference等类。 总的来说,CAS适用于对单个变量进行高效的并发更新,而synchronized适用于保护共享资源的一致性以及对代码块或方法进行同步。根据实际需求和场景的不同,可以选择合适的技术来实现线程安全。 ### 回答3: CAS(Compare and Swap)和synchronized是两种用于实现线程安全的机制。 CAS是一种乐观机制,它使用原子操作来实现线程安全。CAS包含三个操作数:内存位置V、旧的预期值A和要更新的新值B。当执行CAS操作时,只有在内存位置的值与预期值相等时,CAS才会修改内存位置的值为新值B;否则,不做任何操作。由于CAS操作是原子的,所以能够保证只有一个线程能够成功地修改内存位置的值。但如果多个线程同时执行CAS操作,只有一个线程会成功,其他线程需要重试。 synchronized是一种悲观机制,它使用互斥来实现线程安全。在使用synchronized关键字修饰的代码块或方法中,同一时刻只有一个线程可以执行该代码块或方法。其他线程需要等待上一个线程执行完毕并释放后才能执行。synchronized能够保证代码块或方法的互斥访问,避免了并发访问的问题。但由于每次只能有一个线程执行,其他线程会进入阻塞状态,可能会造成性能问题。 总的来说,CAS是一种非阻塞的乐观机制,适用于竞争不激烈的情况,能够提高并发性能。而synchronized是一种阻塞的悲观机制,适用于竞争激烈的情况,能够保证线程安全,但可能降低并发性能。在实际开发中,我们需要根据具体的场景选择适合的机制来确保线程安全和并发性能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值