Java 中的锁:悲观锁、乐观锁、自旋锁、偏向锁、公平锁、可重入锁、独享锁...

23 篇文章 0 订阅
2 篇文章 0 订阅

一、悲观锁、乐观锁

这里的锁,是一种多线程同步操作资源的方式,即悲观方式乐观方式

悲观方式:

  • 加锁,如:synchronized、ReentrantLock

乐观方式:

  • 不加锁,如:concurrent 包的原子类

不加锁是如何保证多个线程同时操作某个资源时,执行结果正确呢?
多个线程同时操作某个资源时,为什么会有问题?
因为执行的操作不是原子操作。
A 线程的操作执行到一半,中间插入 B 的操作,那 A 的执行结果就可能有问题。
如果 A、B 的操作都是原子操作,那就不会有问题。

concurrent 包的原子类,就是将非原子操作变为了原子操作
这个原子操作,就是 CAS。

CAS,即 Compare And Swap,比较和交换。
在 CPU 中通过一个 cpu 指令 cmpxchg 来完成,这是一个赋值操作

它的执行过程如下:

内存中的值:V
对象中的值:V1
对象需重设的值:V2
如果 V、V1 一致:将 V2 赋给 V,赋值成功
如果 V、V1 不一致:将 V 赋给 V1,赋值失败

同时执行 CAS 的线程,只有一个能更新成功,其他都会失败。
因为一旦有一个成功了,那内存中的 V 值就会变化,这就会导致其他线程中的 V1 与 V 不一致,赋值失败。

根据策略的不同,失败之后可以选择重试、报错等行为。
concurrent 包的原子类,如果赋值失败,会通过 while 循环来不断循环重试(即后面要讲的自旋锁)。

乐观锁的好处:不阻塞线程,对于简单的操作,能提高效率。

二、自旋锁、适应性自旋锁

乐观方式下进行

自旋锁:自我循环的乐观锁,即执行 CAS 失败后循环重试。
它的好处就是不阻塞线程,能提高效率。
坏处是如果一直失败,会增加开销。所以一般会有一个自旋次数的限制。

适应性自旋锁:
自旋次数限制不固定的自旋锁。
如果经常成功,那限制次数就多一点。
如果经常失败,那限制次数就少一点。

三、偏向锁、轻量级锁、重量级锁

偏向锁、轻量级锁:乐观方式
重量级锁:悲观方式

偏向锁、轻量级锁、重量级锁是 synchronized 锁的几个等级。

Jdk 6 之前:重量级锁
Jdk 6 及之后:偏向锁、轻量级锁、重量级锁

偏向锁通过标记位对比,减少 CAS 操作;
轻量级锁通过CAS 操作,减少线程阻塞;
重量级锁通过互斥量,将等待线程都阻塞。

当只有一个线程访问锁时,synchronized 的状态是偏向锁,只通过对比 Java 对象头中的一个标记位即可进行操作。
当有第二个线程访问锁时,synchronized 的状态是轻量级锁,两个线程都要通过 CAS 来进行操作。
当有第三个线程访问锁时,synchronized 的状态是重量级锁,三个线程都要通过互斥量获得锁,来进行操作。

四、公平锁、非公平锁

悲观方式下进行

公平锁:遇到锁等待时依次排队
非公平锁:遇到锁等待时先尝试插队(减少线程阻塞消耗),插队失败后再排队

五、可重入锁、不可重入锁

悲观方式下进行

可重入锁:获得锁的线程内,可以再次获得锁,synchronized、ReentrantLock 都是可重入锁。
可重入锁:获得锁的线程内,不能再次获得锁。
可以一定程度上避免死锁。

六、独享锁、共享锁

悲观方式下进行

独享锁:一个对象只能被一个线程加一个锁,持有者可读可写
共享锁:一个对象可以被多个线程加多个锁(只能是共享锁),持有者只能读不能写

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值