乐观锁、悲观锁、CAS、synchronized、AQS、ReentrantLock、Semaphore
文章目录
参考
https://mp.weixin.qq.com/s/WtAdXvaRuBZ-SXayIKu1mA
乐观锁
CAS(Compare And Swap 比较并且替换)
- 是乐观锁的一种实现方式
- 是一种轻量级锁
- JUC 中很多工具类的实现就是基于 CAS 的。
线程在读取数据时不进行加锁,在准备写回数据时,先去查询原值,操作的时候比较原值是否修改,若未被其他线程修改则写回,若已被修改,则重新执行读取流程。
CAS 整体是一个原子操作
例子
AtomicInteger 自增函数 incrementAndGet() 就是这样实现的,其中就有大量循环判断的过程,直到符合条件才成功。
实践
订单表,流水表
一直循环时 的性能问题
ABA 问题
- 线程1读取了数据A
- 线程2读取了数据A
- 线程2通过CAS比较,发现值是A没错,可以把数据A改成数据B
- 线程3读取了数据B
- 线程3通过CAS比较,发现数据是B没错,可以把数据B改成了数据A
- 线程1通过CAS比较,发现数据还是A没变,就写成了自己要改的值
解决方法
增加自增的字段,操作一次就自增加一,或者增加时间戳,比较时间戳的值。
只能保证一个共享变量的原子操作
CAS操作单个共享变量的时候可以保证原子的操作,多个变量就不行了,JDK 5之后 AtomicReference可以用来保证对象之间的原子性,就可以把多个对象放入CAS中操作。
悲观锁
synchronized
synchronized (可以看成)是重量级锁
但是,随着 Java SE 1.6 对 synchronized 进行了各种优化之后,有些情况下它就并不那么重,Java SE 1.6 中为了减少获得锁和释放锁带来的性能消耗而引入的 偏向锁 和 轻量级锁 。
针对 synchronized 获取锁的方式,JVM 使用了锁升级的优化方式,就是先使用 偏向锁 优先同一线程然后再次获取锁,如果失败,就升级为 CAS 轻量级锁,如果失败就会 短暂自旋,防止线程被系统挂起。最后如果以上都失败就升级为 重量级锁。
锁只能升级,不能降级。
AQS(Abstract Queued Synchronizer 队列同步器)
- 实现 ReentrantLock(独占锁) 的基础
- 实现 Semaphore(共享锁) 的基础
-
AQS 有一个 state 标记位,值为1 时表示有线程占用,其他线程需要进入到同步队列等待,同步队列是一个双向链表。
-
当 获得锁 的线程需要等待某个条件时,会进入 condition 的等待队列,等待队列可以有多个。
-
当 condition 条件满足时,线程会从等待队列重新进入同步队列进行获取锁的竞争。
ReentrantLock 内部有公平锁和非公平锁两种实现,差别就在于 新来的 线程是否比 已经在同步队列中的等待线程 更早获得锁。
Semaphore 也是基于 AQS 的,差别在于 ReentrantLock 是 独占锁,Semaphore 是 共享锁。
- 独占锁:一个锁在某一时刻只能被 一个 线程占有
- 共享锁:一个锁可以同时被 多个 线程拥有。