首页 > 编程语言 >【JavaEE】锁策略

【JavaEE】锁策略

时间:2024-08-08 23:23:39浏览次数:10  
标签:重入 加锁 策略 synchronized JavaEE 获取 线程 轻量级

目录

前言

一.悲观锁和乐观锁

二.重量级锁和轻量级锁

三.挂起等待锁和自旋锁

四.公平锁和非公平锁

五.可重入锁和不可重入锁

六.读写锁

synchronized对应的锁策略

1.悲观锁和乐观锁

2.重量级锁和轻量级锁

3.挂起等待锁和自旋锁

4.公平锁和非公平锁

5.可重入锁和不可重入锁

相关面试题

1.你是怎么理解乐观锁和悲观锁的,具体是怎么实现的?

2.介绍下读写锁?

3.什么是自旋锁,为什么要使用自旋锁策略,缺点是什么?

4.synchronized是可重入锁吗?


前言

在前面线程安全中,我们介绍了在多线程中为什么要进行加锁,以及如何使用synchronized进行加锁,那么我可以再来了解一下有哪些锁策略,锁策略不仅仅局限于java,任何和锁有关的话题,都可能会涉及到我接下来讲解的这些。

一.悲观锁和乐观锁

悲观锁:认为发生锁冲突的概率比较大,总是假设最坏的情况,每次取拿数据的时候都认为别人会修改,所以在每次拿数据的时候都会加锁,这样别人想要拿到这个数据,就需要阻塞到它拿到锁为止。

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

悲观锁和乐观锁都是计算机对发生锁冲突的预测。这两个锁并不能说谁优谁劣,而是需要看当前的场景是否使用合适。

举个例子:假如你现在刚考完期末考试,对成绩进行预测。悲观锁的做法:对自己的成绩进行预测,发现自己在做题过程错太多了,导致最后的评估分数过低,那么就会对自己的努力产生怀疑,导致对自己的情绪影响较大。乐观锁的做法:在评估完成绩后,如果成绩过低,考虑自己是不是太粗心了,下次再注意点就行,对自身的情绪并不会有多大的影响。

二.重量级锁和轻量级锁

重量级锁和轻量级锁是站在加锁的开销角度来进行划分的。

重量级锁:依赖于操作系统的Mutex Lock(互斥锁)来实现的,因此状态切换涉及到用户态和内核态的切换,开销较大。适用于线程竞争激烈且锁持有时间长的场景,可以有效避免CPU资源浪费,但线程阻塞和唤醒涉及到系统调用,可能会导致性能问题,尤其是在高并发的环境下。

  • 大量的内核态用户态切换
  • 很容易引发线程调度

轻量级锁:加锁机制尽可能不适用mutex,而是尽量在用户态代码完成,实在搞不定再使用mutex。轻量级锁适用于线程交替执行同步代码块(即互斥操作),如果同一时刻多个线程同时访问同一个锁,则会导致轻量级锁变成重量级锁。

  1. 少量内核态用户态切换
  2. 不太容易引起线程调度

理解用户态 vs 内核态:(可以结合前面线程池讲到的例子线程池

      想象去银行办业务,在窗口外, 自己办理相关业务, 这是用户态,用户态的时间成本是比较可控的。在窗口内, 工作人员做, 这是内核态,内核态的时间成本是不太可控的。如果办业务的时候反复和工作人员沟通, 还需要重新排队, 这时效率是很低的。

轻量级锁在没有锁竞争的情况下性能更优,而重量级锁在锁竞争激烈的环境下比较合适

JVM中的锁机制设计为一个升级过程:无锁 -> 偏向锁  -> 轻量级锁 -> 重量级锁,目的是为了在不同的竞争环境下提供合适的同步机制。

三.挂起等待锁和自旋锁

挂起等待锁是悲观锁和重量级锁的一种典型实现,是一种消极的锁策略。当线程无法获取到锁时,会主动让出CPU并进行等待队列中,直到锁被释放并收到通知后才重新参与线程调度。适用于锁持有时间不确定或者较长的情况,可以节省CPU资源,防止无效的循环等待

自旋锁是乐观锁和轻量级锁的一种典型实现,是一种积极的锁策略。当线程获取锁失败时,并不会让出CPU进行阻塞等待,而是会一直循环尝试获取到锁,直到拿到锁为止。这种优点在于当锁被释放之后,自旋线程能够立即获取到锁,提高了响应速度。但若锁被长时间占用,自旋线程将持续占用CPU资源,导致性能降低

在Java中,这两种锁策略的表现形式并不是直接作为API提供,而是JVM在实现锁机制时采取的策略。这两种锁的选用取决于具体的应用场景及需求。如果线程之间交替比较频繁且锁持有时间不长,那就可以使用自旋锁,若线程之间交互较少,且锁持有时间较长,那就可以使用挂起等待锁。

四.公平锁和非公平锁

公平锁:遵循先进先出原则的一种锁机制,当多个线程尝试获取同一个锁时,会按照线程请求锁的先后顺序来决定哪个线程可以获取到锁。在公平锁中,多个线程获取锁时,会先让等待最久的线程先获取到锁,不会有线程饿死的问题,确保了每个等待锁的线程最终都能获取到锁,提高了线程调度的公平性。但由于每次锁释放都需要唤醒队列中等待最久的线程,这会增加线程上下文切换的开销,特别是在高并发的场景下。

非公平锁:遵循概率均等原则的一种锁机制。非公平锁机制下不保证线程获取锁的顺序与请求锁的顺序是一致的,当锁被释放后,任何等待的线程都可以获取到锁,即使是刚开始等待的线程,也可能先获取到锁。这会导致部分线程长期获取不到锁,形成线程饿死。这种策略在某些场景下可以提高性能,不需要维护等待队列。

公平锁和非公平锁没有好坏之分,还是需要看具体的应用场景。在Java中,java.util.concurrent.locks.ReentrantLock类可以指定是否启用公平锁,通过构造函数传入true参数即可创建公平锁实例,否则默认为非公平锁Java的并发包中,公平锁强调的是线程获取锁的有序性和公平性,而非公平锁则倾向于提高系统的并发性能,但可能会使得部分线程在竞争锁时面临不公平待遇。

五.可重入锁和不可重入锁

可重入锁:指一个线程可以对同一把锁加多次锁,而不会引起死锁。说明当一个已经持有锁的线程再次进行请求获取同一把锁时,不会被阻塞,而是可以得到许可并继续执行。

可重入锁内部会维护一个计数器,记录当前线程获取到锁的次数,每获取到一次锁,计数器就会+1,每释放一次锁,计数器就会-1,当计数器为0时,锁才被真正释放。支持递归调用和嵌套同步,降低了死锁风险

不可重入锁:线程一旦获取到锁之后,若再次请求则会陷入阻塞,无法获取到锁。即一个线程不能再次获取自己已经持有的锁。不允许进行二次加锁,因此在递归调用或者嵌套同步的场景下容易死锁。

可重入锁相较与不可重入锁在除了多层同步和递归调用时具有更高的灵活性和安全性行,不易引发死锁问题。但在某些特定场景下,不可重入锁也可能因其简洁性和性能特点而被选用。并且Java中的synchronized 就是可重入锁.

六.读写锁

我们一般对数据就是进行读操作和写操作,在单线程中,读和写操作并不会产生线程安全问题,但是在多线程中可能就会出现一些线程安全问题,当一个线程在读数据,另一个线程在写数据,那么最后读取的数据和数据 本身不一致;若两个及以上的线程同时在进行写操作,最后得到的结果也不是正确的结果。而多个线程在读数据时是不会有线程安全问题的。那么如何解决这种情况呢?

读写锁因此产生,读写锁是一种比互斥锁更细粒度的锁,区分读操作和写操作允许多个线程同时进行读操作,但不允许同时进行写操作。若有多个线程同时进行读写操作或者同时进行写操作时,读写锁就是互斥的,后面的线程无法获取到锁,保证数据一致性

特点

  1. 读读不互斥:多个线程可以同时读取共享资源,读操作没有线程安全问题。
  2. 读写互斥:若一个线程读,另一个线程写,则会有线程安全问题。
  3. 写写互斥:若两个线程同时进行写操作,则也会有线程安全问题。

synchronized对应的锁策略

1.悲观锁和乐观锁

synchronized是自适应锁,初始时使用乐观锁策略,当发现锁竞争比较激烈时,就会自动切换成悲观锁。

2.重量级锁和轻量级锁

对于重量级锁和轻量级锁,synchronized也是属于自适应锁。开始的时候为轻量级锁,当发现锁竞争比较频繁时,就会切换为重量级锁。

3.挂起等待锁和自旋锁

对于挂起等待锁和自旋锁来说,synchronized也是自适应锁。当锁冲突较小时,synchronized就为自旋锁,当锁冲突较大时,就会切换为挂起等待锁。

4.公平锁和非公平锁

synchronized属于非公平锁。在多线程中,当多个线程尝试获取锁,synchronized不管先后顺序,而是所有竞争锁的线程一起竞争。

5.可重入锁和不可重入锁

synchronized属于可重入锁。当线程获取到锁后,此时若再对同一把锁加锁,不会陷入死锁状态。

通过维护一个计数器,当计数器为0时就释放锁。synchronized不是读写锁是互斥锁

相关面试题

1.你是怎么理解乐观锁和悲观锁的,具体是怎么实现的?

悲观锁认为多个线程访问同一个共享变量冲突的概率比较大,会在每次访问共享变量之前都真正加锁。

乐观锁认为多个线程访问同一个共享变量冲突的概率不大,并不会真的加锁,而是会尝试访问数据。在访问数据的同时识别当前数据是否出现访问冲突。

悲观锁的实现就是先加锁(比如借助操作系统提供的mutex),获取到锁再操作数据,获取不到锁就进行等待。

乐观锁的实现可以引入一个版本号,借助版本号识别出当前的数据访问是否冲突。

2.介绍下读写锁?

  • 读写锁就是把读操作和写操作分别进行加锁。
  • 读锁和读锁之间不互斥。
  • 读锁和写锁之间互斥。
  • 写锁和写锁之间互斥。
  • 读写锁最主要用在“频繁读,不判断写”的场景中。

3.什么是自旋锁,为什么要使用自旋锁策略,缺点是什么?

如果获取锁失败,立即再尝试获取锁,无限循环,直到获取到锁为止,第一次获取锁失败,第二次的尝试会在极短的时间内到来,一旦锁被其他线程释放,就能在第一时间获取到锁。

相当于挂起等待锁。

优点:没有放弃CPU资源,一旦锁被释放就能第一时间获取到锁,更高效,在锁持有时间比较短的场景下非常有用。

缺点:如果锁持有时间较长,就会浪费CPU资源。

4.synchronized是可重入锁吗?

是可重入锁。

可重入锁就是指在连续两次加锁不会导致死锁。

实现方式就是在锁中记录该锁持有的线程身份,以及一个计数器(记录加锁次数),如果发现当前加锁的线程就是持有锁的ixanc,则直接计数自增。


本篇就先到这里了~

若有不足,欢迎指正~

标签:重入,加锁,策略,synchronized,JavaEE,获取,线程,轻量级
From: https://blog.csdn.net/zhyhgx/article/details/141005973

相关文章

  • 策略模式揭秘:如何让飞书、企业微信、钉钉的入职与生日祝福更智能?
    继上一篇飞书、企业微信、钉钉如何精准推送入职与生日祝福背后的数据魔法之后,今天在此基础上分享下策略模式。策略模式是一种行为型设计模式,在工作中使用的频次非常高。生日祝福,入职周年祝福等,每一种都是一种不同的策略。不了解背景的人可以先去看看入职周年祝福与生日......
  • 如何抵挡Temu半托管?提升全托管效果的测评号策略
    2024年,跨境电商的市场风向又变了!1月4日,阿里旗下的速卖通推出半托管模式,通过免佣金和现金补贴吸引卖家;同月,拼多多的Temu也在美国上线了半托管服务,TikTokShop和SHEIN紧随其后。这给才流行不久的全托管模式商家增加了不少压力,尽管全托管模式能提供全面的运营支持,但在竞争激烈的......
  • 【JavaEE初阶】常见的锁策略
    目录......
  • Java多线程编程中的常见问题及优化策略
    Java多线程编程中的常见问题及优化策略大家好,我是微赚淘客返利系统3.0的小编,是个冬天不穿秋裤,天冷也要风度的程序猿!多线程的基本概念在Java中,多线程是指程序中可以同时运行多个线程,每个线程可以执行不同的任务。多线程可以提高程序的执行效率,但同时也带来了一些挑战。线程安全......
  • 深入理解淘客返利系统中的数据加密与隐私保护策略
    深入理解淘客返利系统中的数据加密与隐私保护策略大家好,我是微赚淘客返利系统3.0的小编,是个冬天不穿秋裤,天冷也要风度的程序猿!在现代互联网环境中,数据加密与隐私保护已经成为开发者必须重视的重要问题。本文将深入探讨淘客返利系统中的数据加密与隐私保护策略,并通过Java代......
  • MySQL 常见日志清理策略
    前言:MySQL数据库服务器使用多种类型的日志来记录操作和事件,这对于故障诊断、审计和性能分析非常重要。然而,这些日志文件会随着时间的推移而不断增长,可能会占用大量的磁盘空间。因此,定期清理这些日志是必要的,本篇文章我们一起来学习下如何清理MySQL中的日志文件。二进制日志(......
  • MySQL线上查询性能调优:深入解析与实战策略
    MySQL线上查询性能调优:深入解析与实战策略在数据库管理的日常工作中,确保MySQL线上查询的高效执行是至关重要的。随着数据量的不断增长和查询复杂度的提升,性能调优成为了数据库管理员(DBA)和开发人员必须掌握的技能。本文将从多个维度深入解析MySQL线上查询性能调优的策略和技......
  • 告别Hugging Face模型下载难题:掌握高效下载策略,畅享无缝开发体验
    告别HuggingFace模型下载难题:掌握高效下载策略,畅享无缝开发体验Huggingface国内开源镜像:https://hf-mirror.com/里面总结了很多下载的方法,下面进行一一讲解方法一:网页下载在模型主页的FilesandVersion中中可以获取文件的下载链接。无需登录直接点击下载,还可以复制下载......
  • 一文速通Redis常见问题,带你深入了解Redis数据结构、分布式锁、持久化策略等经典问题。
    本文参考资料:黑马Redis讲义本文参考资料:JavaGuide,guide哥的八股内容个人思考的Redis实践,面试问题的总结,反思目录Redis五大数据结构String1.String数据结构(SDS)2.String应用场景3.Hash与String存储对象的区别SetListHashSortedSetRedis三种特殊数据结构BitMap(位图)......
  • YoloV9改进策略:注意力机制改进|通过iAFF模块优化RepNBottleneck结构,YoloV9性能飞跃|即
    摘要在深度学习和计算机视觉领域,YoloV9以其卓越的目标检测性能赢得了广泛的关注与应用。为了进一步提升YoloV9的识别精度和鲁棒性,我们创新性地在其核心组件——RepNBottleneck模块中引入了迭代注意力特征融合(iAFF)模块。这一改进不仅实现了显著的涨点效果,还展现了在复杂场景......