简述乐观锁和悲观锁——Java

悲观锁和乐观锁

悲观就是任何事都认为会往坏处发生,乐观就是认为任何事都会往好处发生。

打个比方,假如一个公司里只有一台打印机,如果多个人同时打印文件,可能出现混乱的问题,他的资料打印在了我的资料上,悲观锁就是我认为一定会出问题,所以我在使用打印机的时候直接把打印机霸占了,不允许其他人打印文件。这就是悲观锁。

同样的,我认为即使是多个人一起打印,打印机也不会出现混乱打印的情况,所以我不对打印机做出处理,而是确认下每次打印的东西即可。

Java如何实现

悲观锁

悲观锁实现主要靠 synchronized 和 ReentrantLock 给资源加锁,确保资源只分配给一个人,比如我独占打印机。等我打印完了才放开。

synchronized 可以修饰方法体,对象和变量,表示同时只有一个线程能访问。

public void performSynchronisedTask() {
    synchronized (this) {
        // 需要同步的操作
    }
}

private Lock lock = new ReentrantLock();
lock.lock();
try {
   // 需要同步的操作
} finally {
    lock.unlock();
}

乐观锁

乐观锁可以通过 版本号机制和CAS 算法实现。

版本号机制,可以在数据库添加一个数据版本号,当对数据进行修改时同时修改版本号,比如我对这条数据进行了修改,同时对版本号进行+1,可以表示这条数据被修改了几次。

举个例子,假如线程 A 对数据进行修改,首先先读取数据库的版本号,假设此时为1,然后执行修改操作,此时线程 B 也要对数据库进行修改,获取到版本号也为1,然后线程 A 完成了修改,并将版本号修改为 2。之后线程B完成修改时用获取到的版本号和现在的版本号进行对比,结果不用同,所以线程B 对数据库的修改回滚。

CAS 算法

CAS 算法全称是 Compare And Swap(比较与交换),是一个原子操作,当操作一旦开始就不能被打断。

CAS 主要包含三个操作数:V 要更新的变量值,E 预期值,N 要写入的新值

举个例子。假设线程 A 对数据进行修改,想要将数字 6 修改为2,那么此时获取到 CAS 的三个操作数为:V=6,E=6,N=2。此时线程 B 也想对其进行修改,将 6 改为 4,那么它获取到的三个操作数分别为:V=6,E=6,N=4。线程A 首先完成了对数据的修改,获取新的 V,然后用 E 和 V 进行比较,如果相等,则表示没有被修改,所以可以继续完成修改,将 6 改为 2。之后 线程B 也要对其进行修改,获取到新的 V 是 2,但他的预期值是 6,二者不等,所以认定为被其他线程修改了,所以此次修改失败,可以重试,也可以放弃。

 具体是实现是通过 Atomic 原子类进行操作的,可以去详细看一下。

问题

悲观锁

悲观锁加锁,独占打印机就会导致其他人不能打印资料,如果要打印的人很多,这就会造成阻塞问题。

乐观锁

版本号机制,高并发情况下,会导致版本号频繁变动,需要额外的开销去维护版本号。

CAS ,最常见的问题就是 ABA 问题,即当线程 A 进行修改时,其他的线程将这个元素改成了其他数字又改了回来。解决思路就是在变量前面加上版本号或者时间戳。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值