常见的锁策略

本文详细介绍了Java中的锁策略,包括悲观锁与乐观锁的概念及其在Java中的实现(如synchronized),以及重量级锁与轻量级锁、自选锁与挂起等待锁、可重入锁与不可重入锁、公平锁与非公平锁的区别。着重强调了读写锁在并发场景中的优势。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一:什么是锁策略???

策略就是方法,如何做.
锁策略就是在加锁/解锁/遇到锁冲突的时候,都会怎么做.

二:常见的锁策略

2.1:悲观锁 VS 乐观锁

加锁的时候,JVM能够预测当前锁冲突的概率是大还是小.
预测当前锁冲突概率比较大,后续要做的工作往往就会更多,加锁的开销(时间,系统资源)就更大=>悲观锁
预测当前锁冲突概率不大,后续要做的工作往往就更少,加锁的开销(时间,系统资源)就更小=>乐观锁

悲观锁往往是要通过内核来完成一些操作的,要做的工作就多.
乐观锁往往是纯用户态的一些操作,要做的工作就更少.

2.1.1:java中使用的synchronized是哪种锁??

synchronized支持自适应,能够自动统计出当前的锁冲突的次数,进行判定当前是锁冲突概率高还是概率低.
当冲突概率低的时候,就是按照乐观锁的方式来执行的(加锁开销小,速度更快)
当冲突概率高的时候,就会升级成悲观锁的方式来执行(做的工作更多)

2.2:重量级锁 VS 轻量级锁

一般来说,悲观锁,往往就是重量级锁,乐观锁就是轻量级锁.
实际交流过程中,这两组概念,可能会混着用,但深究的话,这两组概念的出发点是不同的:加锁的过程中做的事情多,就是重量级锁,加锁的过程中,做的事情少,就是轻量级锁;而乐观锁,悲观锁,是先预测一下,锁冲突的概率是低还是高,

2.3:自选锁 VS 挂起等待锁

自选锁,是轻量级锁的一种典型实现方式.
在这里插入图片描述
自旋锁:一旦锁被释放,就能第一时间拿到锁,拿锁的速度更快,消耗的CPU更多
挂起等待锁:是重量级锁的一种典型实现方式.
借助系统中的线程调度机制,当尝试加锁,并且锁被占用了,出现了锁冲突,就会让这个尝试加锁的线程,被挂起(阻塞状态),此时这个线程就不参与调度了,直到这个锁被释放,然后系统才能唤醒这个线程,去尝试重新获得锁.
挂起等待锁,消耗的时间更长,一旦一个线程被阻塞了,线程多了,啥时候被唤醒,这个过程是不可控的,可能会经历很长很长的时间.
挂起等待锁:拿到锁的速度更慢,节省CPU.
synchronized轻量级锁部分,基于自旋锁实现(基于CAS机制来实现的);重量级锁部分,是基于挂起等待锁实现(调用系统API,通过内核).
举个栗子:
你向女神表白,然后女神表示,我有男朋友了(女神已经被加锁了)
你的处理方式就有两种了:
(1)自旋锁:仍然每天都会给女神问候早安,午安,晚安,吃了吗?睡了吗?..
一旦女神分手了,此时第一时间就能感知到,于是就更有机会上位.
(2)挂起等待锁:把女神拉黑,从此不再联系,自己每天好好学习,好好健身,做更好 的自己,后来某一天,通过别人,听说,女神分手了,再把女神加回来,再尝试获取锁.
消耗的时间是更多的,女神分手,第几次分手之后,能够加锁成功,是不确定的

2.4:可重入锁 VS 不可重入锁

可重入锁:一个线程,针对一把锁,连续加锁两次及以上,不会死锁
不可重入锁:一个线程,针对一把锁,连续加锁两次及以上,会死锁
synchronized是可重入锁,是因为锁对象里,会记录当前是哪个线程持有了这个锁,但针对这个锁对象进行加锁操作的时候,就会先判定一下,当前尝试加锁的线程,是否是持有锁对象的线程,如果是,直接进行加锁操作,如果不是,那就阻塞等待.同时也要引入计数器,确保成功解锁

2.5:公平锁 VS 非公平锁

当然,公平是基于不同的规则下的.
比如:每个线程获取到锁对象的机会是否均等,均等,就是公平的,不均等,就是不公平的.
此时的公平,是按照先来后到规则制定的.
公平锁:当有多个线程尝试对同一个锁对象进行加锁操作的时候,就会严格按照先来后到的顺序来获得锁对象,哪个线程阻塞等待的时间长,(就是最先阻塞等待的,),哪个线程就先拿到锁对象.
非公平锁:若干个线程,各凭本事,随机的获取到锁,和线程等待时间就无关了.
synchronized属于非公平锁,多个线程,尝试获取锁对象,和线程的等待时间无关了,此时是按照概率均等的方式来进行获取的(系统本身线程调度的顺序就是随机的,如果需要实现公平锁,就需要引入额外的队列,按照加锁的顺序,把这些获取锁的线程入队列,再一个一个的取).

2.6: 互斥锁 VS 读写锁

synchronized 本身就是普通的互斥锁,只有加锁和解锁操作.
读写锁:是一个更特殊的锁:有加读锁,加写锁,解锁操作.
读写锁更具体点就是:
(1):读锁和读锁之间,不会产生互斥(提高了并发能力)(多个线程读同一个变量,不会有线程安全问题)
(2)写锁和写锁之间,会产生互斥.
(3)读锁和写锁之间,会产生互斥.

读写锁突出体现的是"读加锁和读加锁"之间是共享的(不会互斥的),有利于降低锁冲突的概率,提高并发能力.
Java标准库/操作系统API也提供了读写锁的实现.
日常的开发中,有很多场景,属于"读多,写少",大部分都是读操作,偶然有写操作,如果使用普通的互斥锁,此时,每次读操作,都会互斥,就会影响效率,如果使用多谢锁,就能够有效的降低锁冲突的概率,提高效率.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

十一.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值