当前位置: 首页 > news >正文

常见的锁策略

目录

一、乐观锁与悲观锁

二、重量级锁与轻量级锁

三、自旋锁

四、公平锁与不公平锁

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

六、读写锁


一、乐观锁与悲观锁

乐观锁:乐观锁认为在大多数情况下,数据一般不会并发冲突,所以在数据进行提交更新的时候,才会正式对数据是否产生并发冲突进行检测,如果发现并发冲突了,则让返回用户错误的信息,让用户决定如何去做。

悲观锁:悲观锁认为在并发环境下,数据冲突是常态。因此,它会在数据被读取时就加锁,就每次拿数据的时候都会进行一次上锁,这样别人想拿这个数据就会阻塞直到它拿到锁。

        例如在生活职场上,有一个比较重要的项目需要小组去做,而在流程进行中,有一些东西需要请示领导,而在这个时候有俩个员工,分别是员工A和员工B都需要去请示一次领导,员工A认为领导是比较忙的,所以在员工需要请示领导的时候首先在微信跟领导说一下(相当于加锁操作),得到肯定的答复之后,才会去领导的办公室进行商讨,如果得到的是否定的答复,那么就等待一段时间,再进行时间的确认,这个就是悲观锁。

        而员工B认为领导是比较闲的,所以找他商讨项目的事情大概率是有空解答的。因此员工B直接去领导办公室找他,如果领导在的话,问题就解决了,如果不在,就下次再来(虽然没加锁, 但是能识别出数据访问冲突). 这个是乐观锁.​。

        俩个锁的各有其优缺点。

        如果领导确实比较忙,那么使用悲观锁的策略更合适,使用乐观锁就会导致“白跑很多趟”,消耗的资源比较多。

        如果领导确实比较闲的话,那么乐观锁的策略更合适,悲观锁会让效率比较低。

        而在java中,Synchronized 初始使用乐观锁策略. 当发现锁竞争比较频繁的时候, 就会自动切换成悲观锁策略.​

二、重量级锁与轻量级锁

        锁的核心特性 "原子性", 这样的机制追根溯源是 CPU 这样的硬件设备提供的.​

重量级锁
特点

        基于内核:重量级锁的实现依赖于操作系统的内核机制。当线程尝试获取一个已经被占用的重量级锁时,线程会被挂起(阻塞),并进入内核态等待锁的释放。

        性能开销大:由于涉及到用户态与内核态的切换,以及线程的挂起和唤醒操作,重量级锁的性能开销较大。每次加锁或解锁都需要消耗较多的系统资源。

        适用场景:适用于锁持有时间较长的场景,因为在这种情况下,重量级锁的开销相对锁的持有时间可以忽略不计。

轻量级锁

        轻量级锁是一种优化的锁机制,旨在减少锁操作的开销,提高多线程程序的性能

特点

        基于用户态:轻量级锁的实现主要在用户态完成,尽量避免进入内核态。当线程尝试获取一个已经被占用的轻量级锁时,线程不会立即被挂起,而是会尝试通过自旋(Spin)等方式等待锁的释放。

        性能开销小:由于减少了内核态切换和线程阻塞的开销,轻量级锁在锁竞争不激烈的情况下性能更高。

        适用场景:适用于锁持有时间非常短的场景,因为在这种情况下,自旋等待锁释放的开销比线程阻塞和唤醒的开销要小。

synchronized 开始是一个轻量级锁. 如果锁冲突比较严重, 就会变成重量级锁.​

三、自旋锁

        线程在抢锁失败后进入阻塞状态,放弃 CPU,需要过很久才能再次被调度.。这样就可以使用挂起等待锁(就不尝试抢锁,等待锁释放,通过其他的方式进行抢锁)。

        而自旋锁是什么,就类似于无限循环,如果拿锁失败,会在极短的时间内再次尝试拿起锁。伪代码如下:

 while (抢锁(lock) == 失败) {}

         比较适用于锁释放时间短的场景,如果抢锁失败,过不了多久锁会被释放,那就没必要放弃cpu,这个时候就可以使用自旋锁来处理这样的问题.。

四、公平锁与不公平锁

        公平锁与非公平锁是什么呢?公平锁讲究的是“先进先出”,而非公平锁就不讲究“先进先出”。而在线程调度这边,是随机调度的,我们可以认为这是不公平的,如果想要让其变公平,则需要通过一些额外数据结构,记录其线程调度顺序,来让其公平,否则就不行。

          synchronized 是非公平锁。    

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

        

        可重入锁的字面意思是“可以重新进入的锁”,即允许同一个线程多次获取同一把锁。简单理解就是,可重入锁不至于把“自己”锁死,而不可重入锁有可能会把自己锁死。

        例如:java中synchronized 是可以重入的。

synchronized (object1){
    synchronized(object1){
        try {
             object1.wait(1000);
        } catch (InterruptedException e) {
             throw new RuntimeException(e);
        }
    }
}

        在上面的代码中,用了俩次synchronized,其实效果和用一次是一样的,因为在Java里面的锁是可重入的,而不可重入锁如一个简单的互斥锁(Mutex),但在性能这边,不可重入锁的性能会更好,而缺点就是不能存在于递归的场景。

 

六、读写锁

        读写锁是一种特殊的锁,它允许多个线程之间同时读取共享资源,但写入操作是互斥的。也就是说,在写入操作时,任何线程(无论是读线程还是写线程)都不能访问共享资源

        总的来说:

        俩个线程都只是读一个数据,此时并没有线程安全问题,直接并发的读取即可。(读加锁与读加锁,不互斥

        俩个线程都要写一个数据,有线程安全问题。(写加锁和写加锁,互斥

        一个先后曾读另外一个先后曾写,也有线程安全问题。(读加锁和写加锁之间,互斥

        Java标准库中提供了提供了ReentrantReadWriteLock类,实现了读写锁,其中ReentrantReadWriteLock.ReadLock类表示一个读锁.这个对象提供了lock/unlock方法进行加锁解锁.,还提供了ReentrantReadwriteLock.WriteLock类表示一个写锁,这个对象也提供了lock/unlock方法进行加锁解锁.

相关文章:

  • angular获取roleFormGroup的control值
  • k8s scheduler几种扩展方式的关系及区别
  • RDMA的挑战与限制
  • 远程办公新体验:用触屏手机流畅操作电脑桌面
  • PhotoScissors快速抠图与背景填充
  • 快速入手-基于Django-rest-framework的ModelViewSet终极版(七)
  • redis常见面试题
  • Java全栈面试宝典:多线程与Spring核心机制深度解析
  • Unix/Linux 中 dup、dup2 和 dup3 系统调用解析
  • Windows10清理机器大全集
  • parallelStream线程问题及解决方案
  • 内网服务器无法通过公网地址访问映射到公网的内网服务
  • SQL问题分析与诊断(8)——关键信息(1)
  • E5071C数据保存教程:SNP文件/CSV导出+远程传输步骤一键收藏
  • 蓝桥与力扣刷题(蓝桥 最少刷题数)
  • 2025企业级项目设计三叉戟:权限控制+错误监控+工程化提效实战指南
  • 手动搭建并配置react项目(webpack5)
  • 运筹说 第134期 | 矩阵对策的解法
  • 故障识别 | 基于改进螂优化算法(MSADBO)优化变分模态提取(VME)结合稀疏最大谐波噪声比解卷积(SMHD)进行故障诊断识别,matlab代码
  • 深度学习Note.4(机器学习实践)
  • 用个人的信息备案网站吗/人民日报最新新闻
  • 珠海移动网站建设报价/seo页面优化的方法
  • 自己做网站怎么推广/电商运营推广是做什么的
  • 网站开发 毕业设计/百度指数官网数据
  • 北京网站建设net2006/长沙网站排名推广
  • 做网站开发团队/百度搜索引擎网址格式