悲观锁和乐观锁的必要了解

锁是一个十分重要的知识点,在多线程开发中是必不可少的。

锁是用来实现并发控制的,而并发控制则是为了保证数据库的一致性和隔离性不被破坏。

首先判断是否要加锁,那么要先判断是要加悲观锁还是乐观锁。悲观锁和乐观锁使用场景的区别简单来说就是悲观锁适用于资源竞争冲突较多的场景,而乐观锁适用于资源竞争少的场景。悲观锁和乐观锁并非一个具体的类或对象,而是一种设计思想。

悲观锁和乐观锁

悲观锁顾名思义,想法比较悲观,会假设操作数据的时候有其他的人同时修改,因此它每次修改数据都会给数据加锁防止其他人同时操作。一般有sychronized和ReentrantLock等实现方式。也可进一步分为重量级锁、轻量级锁和偏好锁。

乐观锁则是认为每次操作数据的时候都不会有人同时操作,因此只在提交修改的时候判断数据是否被修改过。乐观锁主要靠CAS和版本号控制来实现。

 乐观锁的实现方式

首先是版本号控制,也就是每次操作都会校验修改后的数据的版本号和一开始获得的数据的版本号。在对数据的操作完成后,判断当前版本号是否有变化,如果没有变化则提交修改并让版本号加一,如果有变化则提交失败。从而防止操作覆盖。

其次则是CAS,CAS是一种用来实现自旋锁的算法,全名是CompareAndSwap(JDK1.5之后还有CompareAndSet)。它每次会判断当前值和期望值是否相同,如果相同则更新值。从而保证操作的原子性。但这种方式也有可能引发ABA问题,也就是将A修改为B后再修改回A,那么它也会认为是相同的,可是真的相同吗?JDK1.5之后的CompareAndSet方法就会判断引用是否相同,如果完全相同才会修改成功。

悲观锁的实现方式

使用sychronized修饰代码块或者方法,可以实现悲观锁。但它没有Lock灵活,使用ReentrantLock可以手动加锁和解锁,使用较为规范灵活。

sychronized锁中包含了重量级锁、轻量级锁和偏好锁,用来应对多线程竞争、线程交替运行和单线程运行的情况。重量级锁需要切换用户态和内核态以及上下文切换,成本较高,性能较低。

轻量级锁使用对象头实现,当加锁的时候在栈中创建一个Lock Record,然后执行一次CAS将Lock Record的地址存放到对象的mark word中,如果成功则获得锁,失败则说明需要升级为重量级锁,会进行锁升级。成功之后再重入锁会再创建一个Lock Record,将第一部分设置为null。

偏好锁:一段很长的时间内只被一个线程持有锁时可以用该锁,在第一次获得锁的时候会将线程id设置到对象头的mark word中,并将偏向锁的id改为1。之后再重入锁时只需要判断线程id是否是自己就可以了,性能较高。

乐观锁和悲观锁的优缺点

乐观锁不需要进程阻塞,因此也被称作非阻塞同步,它不需要耗费进程阻塞和唤醒切换以及内核态切换所占用的CPU资源,因此性能较高。但如果资源的竞争过多无法拿到锁,会导致乐观锁一直循环,一直等待重试,那么它循环所占用的CPU资源就可能会很大。不适用于长期无法获取锁的情景。

悲观锁性能则比较低,因为它会对读写都加锁,降低性能的同时保证事务的一致性和隔离性和原子性。但虽然悲观锁性能较低,也避免了乐观锁不断自循环的弊端。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值