【JavaEE】——各种“锁”大总结

8e19eee2be5648b78d93fbff2488137b.png

阿华代码,不是逆风,就是我疯,

你们的点赞收藏是我前进最大的动力!!希望本文内容能够帮助到你!

目录

一:乐观锁和悲观锁

1:乐观锁

2:悲观锁

3:总结

二:轻量级锁和重量级锁

1:轻量级锁

2:重量级锁

3:总结

三:自旋锁和挂起等待锁

1:自旋锁

2:挂起等待锁

四:普通互斥锁和读写锁

1:普通互斥锁

2:读写锁

(0)知识联系

(1)此处解释

(2)总结

3:为什么引入读写锁

(1)场景一

(2)场景二

(3)优化

五:公平锁和非公平锁

1:两者对比

六:可重入锁和不可重入锁

七:synchronized自适应阶段

1:偏向锁阶段

2:轻量级锁阶段

3:重量级锁阶段

4:总结

八:锁消除

九:锁粗化

1:“粒度”

2:定义


一:乐观锁和悲观锁

1:乐观锁

在加锁过程中,预估发生锁冲突的概率小,降低加锁的工作量,加锁的效率就提高了,安全系数不高(可能会引发占用大量cpu资源的问题)

2:悲观锁

在加锁过程中,预估发生锁冲突的概率大,提升加锁的工作量,加锁的效率就下降了,但是安全系数高

3:总结

乐观锁——牺牲安全性换来效率

悲观锁——牺牲效率换来安全性

二:轻量级锁和重量级锁

引入:在乐悲观的基础上延伸出来的

1:轻量级锁

加锁的开销小,速度快——一般指乐观锁

2:重量级锁

加锁的开销大,速度慢——一般指悲观锁

3:总结

乐/悲观锁是加锁前对没有发生的事情的预估

轻/重量锁是加锁后对结果的评价

整体上来说,两者都是在对一件事情进行描述

三:自旋锁和挂起等待锁

1:自旋锁

自旋锁是轻量级锁的一种实现,也是乐观锁,通过与一个while循环搭配,如果获取到锁,那就结束循环;如果没有获取到锁,不会阻塞放弃cpu,而是继续下一次循环,直到获取到锁

(1)使用场景:锁冲突不激烈

(2)优点:其它线程一旦释放锁,就能快速获取到锁

(3)缺点:会占用消耗大量的cpu资源

2:挂起等待锁

挂起等待锁是重量级锁的一种实现,也是悲观锁,锁释放后并不能第一时间获取到锁,而是要通过操作系统的内核进行调度去获取锁,这个等待的过程时间较长。

(1)使用场景:锁冲突激烈

(2)优点:在内核调度的等待时间中,cpu可以做别的事情,即降低了cpu的资源消耗

(3)缺点:不能第一时间获取到锁

四:普通互斥锁和读写锁

1:普通互斥锁

与synchronized相似,可以进行加锁和解锁

2:读写锁

(0)知识联系

想想之前文章写到的MySQL事务处理——三读

“脏读”——给写加锁(写的时候不能读)

“不可重复读”——给读加锁(读的时候不能写)

“幻读”——读写都加上锁

(1)此处解释

读锁和读锁之间,不会发生锁冲突(不会阻塞)

写锁和写锁之间,会发生锁冲突(会阻塞)

读锁和写锁之间,会发生锁冲突(会阻塞)

(2)总结

一个线程加读锁的时候,另一个线程只能读,不能写

一个线程加写锁的时候,另一个线程只能写,不能读

3:为什么引入读写锁

(1)场景一

两个线程一起读,本身就是线程安全的不互斥,如果使用synchronized,就让两者互斥了

(2)场景二

如果一个线程读,一个线程写,也是不可以的(参考MySQL事务那一章节)

(3)优化

在实际开发中,本身读操作就是非常频繁的,引入读写锁可以大大节省下来“并发读”带来的锁冲突的资源消耗,

五:公平锁和非公平锁

1:两者对比

我们知道线程遵守“随机调度”的原则,所以在在加锁过程中就产生了“锁竞争”这一现象,在“Java”中规定公平就是遵守“先来后到”这一原则,synchronized本身就是非公平锁——一旦解锁,下一个加锁的线程是无法确定的。

所以我们引入队列,记录每个线程的顺序,依次加锁,实现“公平锁”

六:可重入锁和不可重入锁

一个线程对同一个对象加了两次锁,不会产生死锁,根据{}和内置的计数器来确定真正加锁和解锁的位置,synchronized就是可重入锁(推荐看看阿华之前写的那篇《多重入锁》文章哈)

系统自带的锁一般是不可重入锁。

七:synchronized自适应阶段

IDEA中提供的synchronized加锁具有自适应能力,内部会自动评估当前锁冲突的激烈程度,在乐观锁(系列:乐观锁,轻量级锁,自旋锁)和悲观锁(系列:悲观锁,重量级锁,挂起等待锁)中进行筛选。

这也是一种优化方式

1:偏向锁阶段

假设现在有一把偏向锁,A线程拿上了这把锁,不是真正意义上的加锁(假加锁),而是让这把锁对A线程有一个(轻量)标记,如果有其他的线程竞争也想要拿上这把锁,那A就会先一步加锁(真加锁)。

理解:

偏向锁标记是对象里头的一个属性,每个锁对象都有这么一个标记,锁对象首次被加锁都会先进入偏向锁阶段

如果没有锁竞争,那么下次加锁,还是会进入偏向锁阶段。

如果在加锁的过程中遭遇“锁竞争”,那么就会升级为“轻量级锁”阶段,

优点:非必要不加锁,没有锁竞争,偏向锁能大大提高效率

彩蛋:锁先生舔着女神A,女神A吊着锁先生就是不跟他确认关系,忽然有个姑娘B加入进来,想和锁先生确认关系,女神A就慌了~,赶快跟锁先生就确认男女朋友了,像极了爱情~~~

2:轻量级锁阶段

通过“自旋锁”的方式实现,synchronized内部也会统计有多少个线程在“锁竞争”,因为一旦超过某一个线程数量限制,大量的自旋锁会非常消耗cpu资源,此时就会升级为“重量级锁阶段”

优点:其他线程一旦释放锁,就能快速拿到锁

缺点:非常消耗cpu资源

3:重量级锁阶段

承接上文,此时线程放弃自旋锁,进入“阻塞等待”,当解锁后,系统在随机唤醒线程进行加锁。

4:总结

以上synchronized加锁的三个阶段是层层递进升级的,在目前Java中是不能够降级的,

八:锁消除

假如一个线程加了锁,但是一眼看过去这个线程肯定没有线程安全问题,那么在编译器编译代码的时候就会自动“消除锁”,提高效率。

针对一些模棱两可的代码,编译器不知道要不要加锁,统一都不会进行锁消除

九:锁粗化

1:“粒度”

synchronized{}花括号中的代码数量越少,则称锁的粒度越细;代码越多,锁的粒度越粗

2:定义

把多个细粒度的锁,合并成一个粗粒度的锁,就叫锁粗化

锁粗化也是为了提高效率,它跟可重入锁可不一样哦

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值