锁的概述(一)--乐观锁与悲观锁

本文深入探讨了乐观锁和悲观锁的概念,分析了它们在Java并发编程中的应用,包括版本号机制、CAS算法以及synchronized关键字和ReentrantLock等悲观锁的实现。详细讲解了CAS算法的工作原理、优点及缺点,以及如何解决ABA问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.1 简介

乐观锁和悲观锁是数据库中引入名词,可以理解是一种思想,在 Java 并发编程中也有所体现,例如 synchronized 关键字和 ReentrantLock 等都是采用悲观锁。
乐观锁,顾名思义就是看待问题很乐观,每次去拿数据时候,都认为别人没有对数据偷偷摸摸修改过。
悲观锁,则相反,看待问题非常悲观,每次去拿数据,总认为别人会偷偷摸摸对数据进行不可告人的修改,所以为了安全,每次去拿数据时候,都会上锁,别人要想拿数据就会阻塞。

1.2 乐观锁

乐观锁由于其特性,多应用于读多写少的场景,加大整个系统的吞吐量。乐观锁实现有两种实现方式:

  • 版本号机制
  • CAS 算法

1.2.1 版本号机制

在数据库表中加上一个数据库版本号 version 字段,当数据库被修改或者更新时候 version 的值加 1 。简单来说,当线程 A 要更新数据时,在读取数据同时会读取到 version 的值,在提交更新时候,假如读取到的 version 值与当前数据库 version 相等是才更新,否则重试更新操作。

1.2.2 CAS 算法

比较与交换(Compare and Swap),是一种无锁的算法,不同 synchronized 阻塞算法,CAS 是非阻塞的算法,在不加锁的情况下实现多线程之间变量的同步,三个操作数:

  • 内存值 V
  • 旧的预期值 A
  • 要修改的新值 B

只有预期值 A 和内存值 V 相同,将内存值修改为 B。

1.2.2.1 CAS 在 JDK 中的应用
  • 原子类变量,如 java.util.concurrent.atomicAtomicXXX
  • java.util.concurrent 中大多数类在实现时候都直接或者间接使用原子变量类。
1.2.2.2 CAS 原理

CASJava 中靠 Unsafe 这个核心类来实现,Java 无法直接访问底层操作系统,而是通过 JNI 来访问,Unsafe 相当于 JVM 一个后门,提供硬件级别的原子操作。

1.2.2.3.CAS 缺点
  • 循环时间长,开销大(自旋 CAS,如果操作不成功,会一直循环执行,直到成功,如果一直不成功,一直循环,会给 CPU 带来非常大的开销)
  • 只能保证一个共享变量的原子操作(只能对一个共享变量执行操作,但是 JDK5 开始提供了 AtomicReference 类来保证引用对象之间的原子性,你可以把多个变量放在一个对象里来进行 CAS操作 )
  • ABA 问题(1.线程 1 从内存 V 取出 A;2.线程 2 从内存中取出 A,并且执行操作变成 B,接着又把 V 位置数据变成 A;3.线程 1 进行 CAS 操作发现内存还是 A,殊不知它已经被被人(线程 2)偷偷摸摸动过手脚了)。从 Java1.5 开始 atomic 提供类 AtomicStampedReference 来解决 ABA 问题,两步判断,先判断当前引用是否等于预期引用,接着判断当前标志是否等于预期标志,全部相等,才能操作。

1.3 悲观锁

悲观锁由于其特性,多应用于写多读少的场景,JavasynchronizedReentrantLock等独占锁就是典型的悲观锁思想。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值