关于常见的锁策略

简介: 关于常见的锁策略

乐观锁和悲观锁


锁的实现者要预测接下来锁冲突的概率是大还是小, 根据锁冲突概率, 来决定是用乐观锁还是悲观锁.


悲观锁


总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞等待, 直到它拿到锁.


乐观锁


假设线程一般情况下不会产生锁冲突,所以在数据进行提交更新的时候,才会正式对数据是否产生锁冲突进行检测,如果发现锁冲突了,则让返回用户错误的信息,让用户决定如何去做.


总结

悲观锁 : 预测接下来发生锁冲突概率比较大.

乐观锁 : 预测接下来发生锁冲突概率比较小.


通常来说, 悲观锁要做的工作要更多一点, 效率也更低一点. 乐观锁做的工作要更少一点, 效率要更高一点.


重量级锁和轻量级锁


重量级锁 : 涉及到大量的内核态用户态切换, 很容易引发线程的调度.

轻量级锁 : 涉及到少量的内核态用户态切换., 不太容易引发线程调度.


总结

重量级锁, 加锁解锁过程更慢, 更低效.

轻量级锁, 加锁解锁过程更快, 更高效.


注 :


一个乐观锁很可能是个轻量级锁.

一个悲观锁很可能是个重量级锁.


自旋锁和挂起等待锁


自旋锁


产生锁冲突时,线程在抢锁失败后会进入阻塞状态,放弃 CPU,需要过很久才能再次被调度.但实际上, 大部分情况下,虽然当前抢锁失败,但过不了很久,锁就会被释放。没必要就放弃 CPU. 这个时候就可以使用自旋锁来处理这样的问题.

伪代码 :


while (抢锁(lock) == 失败) {}


如果获取锁失败, 会立即再尝试获取锁, 无限循环, 直到获取到锁为止. 第一次获取锁失败, 第二次的尝试会在极短的时间内到来.一旦锁被其他线程释放, 就能第一时间获取到锁.


挂起等待锁


与自旋锁不同, 挂起等待锁获取锁失败后 不会再次尝试获取锁, 而是等系统再次调度到了, 再尝试获取锁.


总结


自旋锁是轻量级锁的一种典型实现, 通常是纯用户态的, 不需要经过内核态.(时间相对更短)

挂起等待锁是重量级锁的一种典型实现, 通过内核的机制来实现挂起等待.(时间更长)


synchronized 的锁策略


synchronized 即是乐观锁, 也是悲观锁, 即是轻量级锁, 也是重量级锁, 轻量级锁部分基于自旋锁实现, 重量级锁部分基于挂起等待锁实现.


synchronized 会根据当前锁竞争的激烈程度, 进行自适应.

如果锁冲突不激烈, 则以轻量级锁 / 乐观锁状态运行.

反之, 以重量级锁 / 悲观锁状态运行.


互斥锁和读写锁


synchronized 只有两个操作 :


  1. 进入代码块, 加锁
  2. 出代码块. 解锁.

synchronized 的加锁没有更细致的划分, 它是互斥锁.


注意, 只要是涉及到 “互斥”, 就会产生线程的挂起等待. 一旦线程挂起, 再次被唤醒就不知道隔了多久了.

因此尽可能减少 “互斥” 的机会, 就是提高效率的重要途径.


读写锁就是减少"互斥"来提高效率, 他能把读和写两种加锁区分开来.


读写锁:


  1. 给读加锁
  2. 给写加锁
  3. 解锁


读写锁中规定 :


  1. 读锁和读锁之间, 不会产生锁竞争, 不会产生阻塞等待.
  2. 写锁与写锁之间, 会产生锁竞争.
  3. 写锁与读锁之间, 会产生锁竞争.


读写锁是非必要不加锁, 更适合一写多读的情况.


Java 标准库提供了 ReentrantReadWriteLock 类, 实现了读写锁.

ReentrantReadWriteLock.ReadLock 类表示一个读锁, 提供了 lock / unlock 方法进行加锁解锁.

ReentrantReadWriteLock.WriteLock 类表示一个写锁, 提供了 lock / unlock 方法进行加锁解锁.


可重入锁与不可重入锁


在一个线程中, 如果对同一个锁对象连续加锁两次, 且不死锁, 就叫做可重入锁, 如果死锁了, 就叫不可重入锁.


来段伪代码来理解下 :


synchronized(lock) {
  synchronized(lock) {
  }
}

这个代码便是加锁两次, 第二次加锁需要等待第一个锁释放, 第一个锁所释放需要第二次加锁成功 并走完代码块, 逻辑上产生了矛盾,产生了死锁.

实际上上述代码并不会死锁, 因为 synchronized 是可重入锁, 那对于不可重入锁, 上述情况就会产生死锁.


相关文章
|
8月前
|
安全 开发工具
防止死锁的关键策略
防止死锁的关键策略包括:避免持有多个锁,按相同顺序获取,设置锁获取超时,减小锁粒度,以及利用死锁检测工具。确保线程安全,减少锁竞争,可提高系统并发性能。
81 1
|
7月前
|
Java 调度
Java多线程编程与并发控制策略
Java多线程编程与并发控制策略
|
8月前
|
C++
多线程(锁策略, synchronized 对应的锁策略)
多线程(锁策略, synchronized 对应的锁策略)
60 2
|
8月前
|
安全 Java 编译器
Java并发编程中的锁优化策略
【5月更文挑战第30天】 在多线程环境下,确保数据的一致性和程序的正确性是至关重要的。Java提供了多种锁机制来管理并发,但不当使用可能导致性能瓶颈或死锁。本文将深入探讨Java中锁的优化策略,包括锁粗化、锁消除、锁降级以及读写锁的使用,以提升并发程序的性能和响应能力。通过实例分析,我们将了解如何在不同场景下选择和应用这些策略,从而在保证线程安全的同时,最小化锁带来的开销。
|
Java 程序员 调度
介绍几个常见的锁策略!
介绍几个常见的锁策略!
72 0
|
Java 编译器 Linux
【多线程】锁策略、CAS、Synchronized
锁策略, cas 和 synchronized 优化过程
|
8月前
|
安全 Java 调度
常见的锁策略
常见的锁策略
|
8月前
|
安全 Java 编译器
常见锁策略
常见锁策略
50 0
|
8月前
|
安全 编译器 Linux
多线程(进阶一:锁策略)
多线程(进阶一:锁策略)
171 0
|
8月前
|
Java 调度 数据库
锁策略之干货分享,确定不进来看看吗?️️️
锁策略之干货分享,确定不进来看看吗?️️️
103 0