Java 中所有的锁

1 乐观锁 VS 悲观锁
锁    适用场景    解释
乐观锁    适合写操作多的场景    乐观锁在 Java 中是通过使用无锁编程来实现,最常采用的是CAS算法,Java原子类中的递增操作就通过CAS自旋实现的。
悲观锁    适合读操作多的场景    悲观锁在 Java 中使用 synchronized 关键字和 Lock 来实现。
悲观锁
悲观锁认为自己在使用数据的时候一定有别的线程来修改数据,因此在获取数据的时候会先加锁,确保数据不会被别的线程修改。在 Java 中使用 synchronized 关键字和 Lock 来实现。

乐观锁
乐观锁认为自己在使用数据时不会有别的线程修改数据,因此在获取数据的时候不会添加锁,只是在更新数据的时候去判断之前有没有别的线程更新了这个数据。如果这个数据没有被更新,当前线程将自己修改的数据成功写入。如果数据已经被其他线程更新,则根据不同的实现方式执行不同的操作(例如报错或者自动重试)。在 Java 中是通过使用无锁编程来实现,最常采用的是CAS算法,Java原子类中的递增操作就通过CAS自旋实现的。

2 自旋锁 VS 适应性自旋锁
自旋锁
阻塞或唤醒一个Java线程需要操作系统切换CPU状态来完成,这种状态转换需要耗费处理器时间。如果同步代码块中的内容过于简单,状态转换消耗的时间有可能比用户代码执行的时间还要长。

在许多场景中,同步资源的锁定时间很短,为了这一小段时间去切换线程,线程挂起和恢复现场的花费可能会让系统得不偿失。如果物理机器有多个处理器,能够让两个或以上的线程同时并行执行,我们就可以让后面那个请求锁的线程不放弃CPU的执行时间,看看持有锁的线程是否很快就会释放锁。

锁    相同    不同
自旋锁    任何时候只能有一个获得锁    如果资源已经被占用,资源申请者只能进入睡眠状态。等到锁被释放否再唤醒资源申请者,CPU再切换到新的资源申请者。
互斥锁    任何时候只能有一个获得锁    如果资源已经被占用,资源申请者不会进入睡眠状态,而是循环等待看锁是否被释放。如果锁被释放就会立即获得锁,而不需要经过唤醒和CPU上下文切换。
优点
在一些同步资源的锁定时间很短的业务中,线程挂起和恢复现的时间会比自旋的时间要长的适合使用自旋锁。

缺点
自旋锁本身是有缺点的,它不能代替阻塞。自旋等待虽然避免了线程切换的开销,但它要占用处理器时间。如果锁被占用的时间很短,自旋等待的效果就会非常好。反之,如果锁被占用的时间很长,那么自旋的线程只会白浪费处理器资源。所以,自旋等待的时间必须要有一定的限度,如果自旋超过了限定次数(默认是10次,可以使用-XX:PreBlockSpin来更改)没有成功获得锁,就应当挂起线程。

自旋锁的实现原理同样也是CAS,AtomicInteger中调用unsafe进行自增操作的源码中的do-while循环就是一个自旋操作,如果修改数值失败则通过循环来执行自旋,直至修改成功。
 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值