乐观锁 VS 悲观锁

简介: 手撕乐观锁 悲观锁

一、简介

  • 悲观锁:总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。再比如Java里面的同步原语synchronized关键字的实现也是悲观锁。
  • 乐观锁:顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制。乐观锁适用于多读的应用类型,这样可以提高吞吐量,像数据库提供的类似于write_condition机制,其实都是提供的乐观锁。在Java中java.util.concurrent.atomic包下面的原子变量类就是使用了乐观锁的一种实现方式CAS实现的。

二、悲观锁的实现

image.png
对象锁:synchronized

锁在某一个实例对象上,如果该类是单例,那么该锁也具有全局锁的概念。

synchronized是对类的当前实例(当前对象)进行加锁,防止其他线程同时访问该类的该实例的所有synchronized块,注意这里是“类的当前实例”, 类的两个不同实例就没有这种约束了。

import java.util.concurrent.TimeUnit;
​
public class Test1 {
​
    public synchronized void start() {
        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + ",start");
    }
​
    public synchronized void end() {
        System.out.println(Thread.currentThread().getName() + ",end");
    }
​
    public static void main(String args[]) {
        Test1 test = new Test1();
        new Thread(test::start, "线程A").start();
        new Thread(test::end, "线程B").start();
    }
}

执行结果:

线程A,start 线程B,end

类锁:static synchronized

该锁针对的是类,无论实例多少个对象,那么线程都共享该锁。

public synchronized void start() {
    try {
        TimeUnit.SECONDS.sleep(2);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    System.out.println(Thread.currentThread().getName() + ",start");
}
​
public synchronized void end() {
    System.out.println(Thread.currentThread().getName() + ",end");
}
​
public static void main(String args[]) {
    StaticTest a = new StaticTest();
    StaticTest b = new StaticTest();
    new Thread(a::start, "线程A").start();
    new Thread(b::end, "线程c").start();
}

执行结果:

线程A,end 线程c,start

更改start、end方法为static后,执行结果:

线程c,start 线程A,end

synchronized+static synchronized

public class StaticTest {
​
    public synchronized void start() {
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + ",start");
    }
​
    public static synchronized void end() {
        System.out.println(Thread.currentThread().getName() + ",end");
    }
​
​
    public static void main(String args[]) {
        StaticTest a = new StaticTest();
        StaticTest b = new StaticTest();
        new Thread(() -> a.start(), "线程c").start();
        new Thread(() -> a.end(), "线程A").start();
    }
}

执行结果:

线程A,end 线程c,start

synchronized 与static synchronized 相当于两帮派,各自管各自,相互之间就无约束了,可以被同时访问。

三、乐观锁的实现

image.png

以上两图来自美团技术团队

CAS算法
CAS是一种无锁算法,CAS有3个操作数,内存值V,旧的预期值A,要修改的新值B。当且仅当预期值A和内存值V相同时,将内存值V修改为B,否则什么都不做。
image.png

这就容易引起ABA问题
一个线程X1从内存位置V中取出A,这时候另一个线程Y1也从内存中取出A,并且Y1进行了一些操作变成了B,然后Y1又将V位置的数据变成A,这时候线程X1进行CAS操作发现内存中仍然是A,然后X1操作成功。尽管线程X1的CAS操作成功,但是不代表这个过程就是没有问题的。

解决方案CAS类似于乐观锁,即每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据。因此解决方案也可以跟乐观锁一样:使用版本号机制,如手动增加版本号字段

目录
相关文章
|
应用服务中间件 索引 nginx
生产环境ES查询延迟排查
最近经常收到业务方配置的ES查询延迟告警,同样的请求手动在Kibana控制台执行只需几十毫秒就返回结果。受影响的整个链路情况如下,php应用程序通过部署在ES集群各节点上的nginx访问ES请求查询数据。
5954 0
|
开发者
Mac 系统偏好设置 安全性与隐私 没有“任何来源” 选项解决办法
Mac 系统偏好设置 安全性与隐私 没有“任何来源” 选项解决办法
5974 0
|
7月前
|
消息中间件 canal 缓存
缓存与数据库一致性终极指南:从入门到放弃?不,到精通!上
凌晨被投诉惊醒?缓存与数据库不一致是常见难题。本文详解五大解决方案:旁路缓存、双删策略、消息队列补偿、Binlog监听与版本号控制,结合场景分析一致性、性能与复杂度的权衡,助你选型不踩坑。
|
机器学习/深度学习 存储
深入理解SVM中的核函数及其应用
深入理解SVM中的核函数及其应用
947 83
|
存储 缓存 安全
内存屏障,写屏障和读屏障
本文介绍了Java内存模型(JMM)的关键概念,包括主内存、工作内存、原子性操作、可见性和有序性,并详细解释了写屏障和读屏障的作用,它们是用来确保多线程环境中内存可见性和指令重排序一致性的同步操作。
553 1
|
存储 自然语言处理 算法
OpenIM Bot: 用LLM构建企业专属的智能客服
OpenIM Bot 通过结合LLM和RAG技术,构建企业专属的智能客服系统。该系统通过优化向量存储、混合检索和查询分析,解决了LLM的幻觉、新鲜度、token长度和数据安全问题,提升了用户体验。向量存储和预处理步骤确保文档高质量,而混合检索结合文本和语义搜索,增强了检索结果的准确性。通过迭代优化,OpenIM Bot 提供了高效、智能的支持服务,减轻了支持团队的负担,提升了问题解决效率。
1638 3
OpenIM Bot: 用LLM构建企业专属的智能客服
|
存储 数据库 数据格式
深入理解依赖倒置原则(Dependence Inversion Principle)
深入理解依赖倒置原则(Dependence Inversion Principle)
1404 0
|
消息中间件 canal 缓存
Redis与MySQL双写一致性如何保证:延迟双删?binlog异步删除?
Redis与MySQL双写一致性如何保证:延迟双删?binlog异步删除?
|
Java 数据格式 微服务
2024最新首发,全网最全 Spring Boot 学习宝典(附思维导图)
📚 《滚雪球学Spring Boot》是由CSDN博主bug菌创作的全面Spring Boot教程。作者是全栈开发专家,在多个技术社区如CSDN、掘金、InfoQ、51CTO等担任博客专家,并拥有超过20万的全网粉丝。该教程分为入门篇和进阶篇,每篇包含详细的教学步骤,涵盖Spring Boot的基础和高级主题。
2619 4
2024最新首发,全网最全 Spring Boot 学习宝典(附思维导图)

热门文章

最新文章