问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

多线程之锁优化:使用乐观锁优化并行操作

创作时间:
作者:
@小白创作中心

多线程之锁优化:使用乐观锁优化并行操作

引用
CSDN
1.
https://m.blog.csdn.net/echohuangshihuxue/article/details/141724176

在多线程编程中,锁机制是保证线程安全的重要手段。除了传统的悲观锁(如Synchronized和Lock),乐观锁作为一种非阻塞型的锁机制,通过CAS(Compare and Swap)算法实现,在高并发场景下展现出更好的性能。本文将深入探讨乐观锁的实现原理、应用场景以及性能优化方案。

什么是乐观锁

乐观锁的核心思想是在操作共享资源时,总是假设自己可以成功完成操作。当多个线程同时操作一个共享资源时,只有一个线程会成功。失败的线程不会像悲观锁一样被挂起,而是可以选择重试或放弃。

乐观锁的实现原理

CAS算法

CAS是实现乐观锁的核心算法,包含三个参数:V(需要更新的变量)、E(预期值)和N(最新值)。只有当V等于E时,V才会被更新为N。如果V与E不同,说明已有其他线程修改了V,此时当前线程不做操作,返回V的真实值。

在JDK的concurrent包中,atomic路径下的类都是基于CAS实现的。以AtomicInteger为例:

public final boolean compareAndSet(int expect, int update) {
    return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}

public final int getAndIncrement() {
    return unsafe.getAndAddInt(this, valueOffset, 1);
}

处理器如何实现原子操作

处理器通过总线锁定和缓存锁定机制来保证复杂内存操作的原子性。现代处理器普遍支持缓存锁定机制,当某个处理器对缓存中的共享变量进行操作时,会通知其他处理器放弃存储该共享资源或重新读取该共享资源。

优化CAS乐观锁

在写大于读的场景下,CAS失败的可能性会增大。为了解决这一问题,JDK 1.8引入了LongAdder类。LongAdder通过将操作分散到多个变量值上,降低了操作共享变量的并发数。其内部由一个base变量和一个cell[]数组组成:

最终结果通过以下公式计算得出:

性能对比测试

在不同场景(读多写少、读少写多、读写相当)和不同竞争级别下,对Synchronized、ReentrantLock、ReentrantReadWriteLock、StampedLock以及乐观锁LongAdder进行了压测。测试结果表明:

  • 在读大于写的场景下,读写锁ReentrantReadWriteLock、StampedLock以及乐观锁的读写性能最好。
  • 在写大于读的场景下,乐观锁的性能最好。
  • 在读写相当的场景下,两种读写锁以及乐观锁的性能优于Synchronized和ReentrantLock。

总结

乐观锁通过CAS算法实现,具有非阻塞特性,在高并发场景下展现出更好的性能。然而,它只能保证单个变量操作的原子性,对于涉及多个变量的操作,仍需依赖悲观锁。在实际应用中,应根据具体场景选择合适的锁机制。

© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号