JavaEE——常见的锁策略

JavaEE传送门

JavaEE

JavaEE——No.1 多线程案例

JavaEE——No.2 多线程案例


目录

  • 常见的锁策略
    • 1. 乐观锁 vs 悲观锁
    • 2. 读写锁 vs 普通互斥锁
    • 3. 重量级锁 vs 轻量级锁
    • 4. 自旋锁 vs 挂起等待锁
    • 5. 公平锁 vs 非公平锁
    • 6. 可重入锁 vs 不可重入锁
    • 7. 偏向锁
    • 8. synchronized
      • synchronized 的优化操作


常见的锁策略

1. 乐观锁 vs 悲观锁

乐观锁

假设数据一般情况下不会产生并发冲突, 预测锁冲突的概率不高, 因此做的工作就可以简单一点.

悲观锁

总是假设最坏的情况, 预测锁冲突的概率较高, 因此做的工作就可以复杂一点.


2. 读写锁 vs 普通互斥锁

普通互斥锁

就如同 synchronized , 当两个线程同一把锁, 就会产生等待.

读写锁

  1. 加读锁
  2. 加写锁
  • 读锁和读锁之间, 不会产生竞争
  • 写锁和写锁之间, 有竞争
  • 读锁和写锁之间, 有竞争

# 注意 #

在日常的需求下, 读的场景往往很多, 写的场景往往更少, 所以, 读写锁相比普通的互斥锁, 就少了很多竞争, 优化了效率.


3. 重量级锁 vs 轻量级锁

重量级锁

加锁解锁的开销比较. 典型的, 进入内核态 (内核比较忙碌) 的加锁逻辑, 开销较大.

轻量级锁

加锁解锁的开销比较. 典型的, 纯用户态的加锁逻辑, 开销较小.

  • 乐观锁和悲观锁是站在加锁的过程上看待的, 加锁解锁过程中干的工作的多少.
  • 而重量级锁和轻量级锁是站在结果的角度上看待的, 最终加锁解锁操作消耗的时间多少.
  • 通常情况下, 乐观锁, 一般也比较轻量. 悲观锁, 一般比较重量. 但也不是绝对的.

4. 自旋锁 vs 挂起等待锁

自旋锁

轻量级锁的一种典型实现

反复尝试获取锁, 直到获取到锁为止. 第一次获取锁失败, 第二次的尝试会在极短的时间内到来. 一旦锁被其他线程释放, 就能第一时间获取到锁.

挂起等待锁

重量级锁的一种典型实现

在等待锁的时间CPU可以做一些其他的事情, 可能锁已经被其他线程释放, 但我没有发现. 获取到锁的时间没那么及时.


5. 公平锁 vs 非公平锁

公平锁

遵守 "先来后到" , B 比 C 先来的. 当 A 释放锁的之后, B 就能先于 C 获取到锁.

非公平锁

不遵守 "先来后到", B 比 C 先来的. 当 A 释放锁的之后, B 和 C 都有可能获取到锁.

# 注意 #

  • 操作系统默认的锁的调度, 是非公平的情况. 如果要想实现公平锁, 就需要依赖额外的数据结构, 来记录线程们的先后顺序.
  • 公平锁和非公平锁没有好坏之分, 关键还是看适用场景.
  • synchronized 是非公平锁.

6. 可重入锁 vs 不可重入锁

可重入锁

同一个线程针对同一把锁, 连续加锁两次, 不会死锁

不可重入锁

同一个线程针对同一把锁, 连续加锁两次, 死锁


7. 偏向锁

类似于懒汉模式, 必要时再加锁, 能不加, 就不加.

你给某个线程夹里, synchronized, 但是这个代码实际过程中, 真的会发生锁竞争吗 ? 不一定 ! !

我们来画图理解一下.
JavaEE——常见的锁策略_第1张图片
JavaEE——常见的锁策略_第2张图片


8. synchronized

  1. 既是乐观锁, 也是悲观锁
  2. 即使轻量级锁, 也是重量级锁
  3. 乐观锁的部分是基于自旋锁实现的; 悲观锁的部分是基于挂起等待锁实现的
  4. 不是读写锁, 是普通互斥锁
  5. 是非公平锁
  6. 是可重入锁
  7. 是偏向锁

synchronized 是自适应的

初始使用的时候, 是乐观锁/ 轻量级锁/ 自旋锁, 如果锁竞争不激烈, 就处于该状态不变.

如果锁竞争激烈, synchronized 就会自动升级成 悲观锁/ 重量级锁/ 挂起等待锁


synchronized 的优化操作

锁膨胀

JVM 将 synchronized 锁分为 无锁、偏向锁、轻量级锁、重量级锁 状态。会根据情况,进行依次升级。

synchronized 加锁的具体过程

  1. 偏向锁
  2. 轻量级锁
  3. 重量级锁

如果当前场景中, 锁竞争不激烈, 则是以轻量级状态来工作. (自旋) , 第一时间拿到锁

如果当前场景中, 锁竞争激烈, 则是以重量级锁状态来进行工作的. (挂起等待) , 拿到锁没那么及时, 但是节省了 CPU 的开销.

锁消除

JVM 自动判定, 发现这个地方的代码, 不必加锁. 如果你写了 synchronized , 就会自动把锁给去掉.

缩消除, 只是在编译器/ JVM 有十足把握的时候才进行的. (如果代码 100% 能消除, 编译器才会给你消除, 如果 80% 的可能能消除, 就不消除了)

锁粗化

锁的粒度, synchronized 对应代码块包含多少代码, 包含的代码少, 粒度细. 包含的代码多, 粒度粗.

锁粗化, 就是把细粒度的加锁 -> 粗粒度的加锁.

频繁的加锁解锁会带来一些额外的锁竞争, 锁粗化后就能节省开销.
JavaEE——常见的锁策略_第3张图片
# 注意 #

锁的粒度不是越粗越好, 要看具体的使用场景, 像上述反复加速的场景, 变粗了确实可以. 但是有时锁太粗不利于多线程并发, 有悖之前写多线程的初衷.


(( ◞•̀д•́)◞⚔◟(•̀д•́◟ ))

以上就是今天要讲的内容了,希望对大家有所帮助,如果有问题欢迎评论指出,会积极改正!!
在这里插入图片描述
加粗样式

这里是Gujiu吖!!感谢你看到这里
祝今天的你也
开心满怀,笑容常在。

你可能感兴趣的:(JavaEE,java,开发语言)