【十一】一文带你迅速掌握锁策略
1. 乐观锁VS悲观锁
锁得实现者,预测接下来锁冲突的概率(同个线程针对同一个对象加锁,产生阻塞等待)大不大,根据这个冲突的概率,来决定接下来该怎么做~
- 乐观锁:看名字就是比较乐观,预测冲突概率不大,所以做的工作会比较少,效率会高一点(并不绝对)
- 悲观锁:就是预测冲突概率比较大,所做的工作比较多,效率会低一点(并不绝对)
乐观锁:假设数据一般情况下不会发生并发冲突,所以在数据进行提交更新的时候,才会正式对数据是否产生并发冲突进行检测,如果发现并发冲突了,则返回用户错误的信息,让用户决定如何去做
悲观锁:总是假设最坏情况,每次对数据都会进行修改操作,所以在每次拿数据的时候都会上锁,这样其他线程想操作这个数据就会阻塞等待
2. 轻量级锁VS重量级锁
synchronized 并不仅仅是对 mutex 进行封装,内部还做了很多其他的工作
synchronized 开始是一个轻量级锁,如果锁冲突比较严重,就会 变成重量级锁
- 轻量级锁:加锁解锁,过程更快更高效,加锁机制尽可能不使用mutex,而是在用户态代码完成,实在搞不定,再使用mutex
- 少量的内核用户态切换
- 不太容易引发线程的调度
- 重量级锁:加锁解锁,过程更慢更低效,加锁机制重度依赖OS提供的mutex
- 大量的内核用户态切换
- 很容易引发线程的调度
3. 自旋锁VS挂机等待锁
自旋锁(轻量级锁的一种典型实现)
- 按照之前的方式,线程在抢锁失败后进入阻塞状态,放弃CPU,需要很久才能再次调度,然后实际上,在大多数情况下,虽然抢锁失败,但过不了多久,锁就会被释放。所以就没必要放弃CPU,这个时候就可以使用自旋锁来处理这种情况。
- 优点:如果获取锁失败后,就会一直尝试获取,无限循环,一直等到获取锁位置!一旦锁被其他线程释放,就会第一时间获取到锁!
- 缺点:会造成忙等,消耗CPU资源
挂起等待锁(重量级锁的一种典型实现)
- 挂起等待锁,当抢锁失败后,就放弃了CPU,进入到等待队列,这个时候如果其他线程释放锁之后,就会被唤醒,但是不能第一时间拿到资源的,可能就会需要很久才能获取到锁!
举两个例子:
例子1(自旋锁):假设你向女神表白,但是你被发了好人卡~,但是你仍旧每天锲而不舍的,向女神发送早安晚安,一旦哪一天,女神和男朋友分手了,此时你就有很大机会,把锁给加上!
例子2(挂起等待所):表白失败后,你就不找女神了,或许在女神分手后某一天,想到了你,就会主动来找你,这个时候你的机会也来了,但是不一样你就是找的第一个~
针对上述三组策略:提出个疑问? synchronized 属于哪一种
synchronized 既是悲观锁,也是乐观锁;既是轻量级锁,也是重量级锁;轻量级锁基于自旋锁实现,重量级锁基于挂起等待锁实现~
具体synchronized 会根据当前锁竞争的激烈程度,自适应
4. 互斥锁VS读写锁
synchronized就是互斥锁,加锁就是单纯的加锁,没有更细化的区分。
但是读写锁,能够把 读 和 写 两种加锁区分开来
在多线程中,数据的读取不会产生线程安全的问题。但是数据的写入互相之间和读之间都需要进行互斥。 此时考虑到一个问题,如果两种情况下都使用同一个锁,就会产生极大的性能消耗,所以读写锁就产生了!
一个线程对于数据的访问,就要是两种:读操作和写操作
- 两个线程都只是进行读操作,没有线程安全问题
- 两个线程都要进行写操作,就会有线程安全问题
- 一个线程读另一个线程写,就会有线程安全问题
所以针对上面读和写操作区别,Java标准库提供了 ReentrantReadWriteLock 类,实现了读写锁
- ReentrantReadWriteLock.ReadLock 类 表示 一个读锁,这个对象提供了 lock/ unlock 进行加锁解锁
- ReentrantReadWriteLock.WriteLock 类 表示 一个写锁,这个对象也提供了 lock/unlock 方法进行加锁解锁
其中:
- 读加锁和读加锁之间,不互斥
- 读加锁和写加锁之间,互斥
- 写加锁和写加锁之间,互斥
读写锁特别适合 “频繁都,不频繁写” 的场景中
5. 可重入锁VS不可重入锁
可重入锁:就是允许同一个线程多次获取同一把锁
不可重入锁:一个线程,针对同一把锁,连续加锁两次,发生死锁
Object locker = new Object();
synchronized(locker){synchronized(locker){// ... }
}
上述代码中,不会产生死锁,因为 synchronized 是可重入锁!在加锁的时候,判断当前申请锁的线程是不是已经是锁的拥有者,如果是,就直接放行了~
下面介绍一些死锁的情况:
- 1.一个线程,一把锁,可重入锁没有事,不可重入锁就会出现死锁
Object locker = new Object();
synchronized(locker){synchronized(locker){// ... }
}
- 2.两个线程,两把锁,即使是可重入锁,也会死锁
Object locker1 = new Object();
Object locker2 = new Object();// t1 线程
synchronized(locker1){synchronized(locker2){//...}
}// t2 线程
synchronized(locker2){synchronized(locker1){//...}
}
上述代码,t1 线程获取 locker1 锁 ,t2 获取 locker2 锁,此时 t1要获取locker2锁时获取不到,要等t2释放以后,而t2要获取 locker1锁 之后,也必须等 t1 释放 locker1,此时就造成了死锁~~
- 3.N个线程,M把锁
这里就有个经典的哲学家,就餐问题
说到这里,我们介绍下产生死锁的四个必要条件:
- 互斥使用:一个线程拿到一把锁之后,另一个线程不能使用
- 不可抢占:一个线程拿到锁,只能自己主动释放,不能被其他线程强行占用
- 请求和保持:一个线程在拿到锁之后,不释放,同时又在申请获取其他的锁
- 循环等待:上述例子,逻辑依赖循环的
如何避免死锁~只要破除其中的任意一个条件就行,这里介绍 破坏循环等待这个条件!
- 就是针对锁进行编号,如果需要同时获取多把锁,约定加锁顺序,务必是先对小的编号进行加锁,后对大的编号加锁!
// 伪代码
Object locker1 = new Object();
Object locker2 = new Object();// 线程A执行下面代码逻辑
synchronized(locker1){synchronized(locker2){//...}
}// 线程B执行下面代码逻辑
synchronized(locker1){synchronized(locker2){//...}
}
此时就不会造成循环等待现象~
6. 公平锁VS非公平锁
假设有三个线程对一个数据进行操作,线程A先获得锁,线程B也在申请获取锁,线程C也在申请获取锁。线程B在线程C之前申请获取锁
- 公平锁:就是当线程A释放锁之后,线程B先线程C之前申请获取锁,此时就是线程B先获得,然后等线程B释放以后,线程C再获得锁
- 非公平锁:就是当线程A释放锁,线程B和线程C获取锁的先后 ,是不确定的,两个可能先于对方
7. synchronized
系统对于线程的调度是随机的,自带的synchronized这个锁是非公平的~
要想实现公平锁需要在 synchronized 的基础上,加上队列,来记录这些加锁线程的顺序
synchronized的特点:
- 既是悲观锁,也是乐观锁
- 既是轻量级锁,也是重量级锁
- 轻量级锁基于自旋锁实现,重量级锁基于挂机等待锁
- 不是读写锁
- 是可重入锁
- 是非公平锁
- 是互斥锁
【十一】一文带你迅速掌握锁策略相关推荐
- 爬虫入门经典(十一) | 一文带你爬取传统古诗词(超级简单!)
大家好,我是不温卜火,是一名计算机学院大数据专业大三的学生,昵称来源于成语-不温不火,本意是希望自己性情温和.作为一名互联网行业的小白,博主写博客一方面是为了记录自己的学习过程,另一方面是总结自己 ...
- 一文带你理解 WireGuard 路由策略
公众号关注 「奇妙的 Linux 世界」 设为「星标」,每天带你玩转 Linux ! ❝ 原文链接
- 教妹学Java(二十一):一文带你了解面向对象编程的所有概念
你好呀,我是沉默王二,是<Web 全栈开发进阶之路>的作者,CSDN 的博客之星.<教妹学 Java>是一套非常有趣的付费专栏,除了继续保持幽默风趣的行风风格,我还力求把每一个 ...
- 一文带你深入理解JVM内存模型
一文带你深入理解JVM内存模型 一.JAVA的并发模型 共享内存模型 在共享内存的并发模型里面,线程之间共享程序的公共状态,线程之间通过读写内存中公共状态来进行隐式通信 该内存指的是主内存,实际上是物 ...
- 还不会ts?一文带你打开ts的大门
一文带你打开ts的大门 序言 一.什么是TypeScript? 1.编程语言的类型 2.TypeScript究竟是什么? 二.为什么要学习TypeScript? 1.程序更容易理解 2.效率更高 3. ...
- 一文带你理解Java中Lock的实现原理
转载自 一文带你理解Java中Lock的实现原理 当多个线程需要访问某个公共资源的时候,我们知道需要通过加锁来保证资源的访问不会出问题.java提供了两种方式来加锁,一种是关键字:synchron ...
- 超详细!一文带你了解 LVS 负载均衡集群!
作者 | JackTian 来源 | 杰哥的IT之旅(ID:Jake_Internet) 前言 如今,在各种互联网应用中,随着站点对硬件性能.响应速度.服务稳定性.数据可靠性等要求也越来越高,单台服务 ...
- 一文带你搞懂C#多线程的5种写法
一文带你搞懂C#多线程的5种写法 1.简介 超长警告! 在学习本篇文章前你需要学习的相关知识: 线程基本知识 此篇文章简单总结了C#中主要的多线程实现方法,包括: Thread 线程 ThreadPo ...
- 进程、线程与协程傻傻分不清?一文带你吃透!
目录 前言 内容大纲 进程 什么是进程 进程的控制结构 进程的状态 进程的上下文切换 线程 什么是线程 线程与进程的对比 线程的上下文切换 线程的模型 调度 调度原则 调度算法 好文推荐 前言 欢迎来 ...
最新文章
- linux中deb怎样安装
- 一般判五年几年能出来_判刑五年能减刑多少年
- 原来流行也可以变成怀旧!
- PHP获取当前页面的URL
- 音视频技术开发周刊 | 233
- namenode启动不了以及datanode启动不了的解决办法
- myjdbc链接数据库.增删改查
- 网络数据库的复制和同步(一)
- 算法-无向图(深度优先搜索和广度优先搜索)
- 大学英语(第四册)复习(原文及全文翻译)——Unit 5 - TO LIE OR NOT TOLIE—THE DOCTOR‘S DILEMMA(撒谎还是不撒谎——医生的难题)
- recovery 工作流程
- 计算机类核心期刊排名(国内)
- Android使用addr2line定位native崩溃堆栈
- 教你如何学模电——三极管篇
- STC89C52RC的P4口的应用问题
- IOS 跨域问题分析和处理
- 提高编程效率的5大VS Code插件
- Twitter整合营销的方法技巧
- 美国证监会给区块链股票降温
- (其他2)VideoPlayer 介绍
热门文章
- 回文日期 php,c语言程序实例大全,220个详细程序源代码
- 粉丝经济大爆发居然真的来了
- 苹果手机投影_投影仪连手机的两种方式介绍
- 神州租车还有这些新玩法?打造了行业新标杆
- 二维数组中的查找(两种解法,各有千秋)
- 黑盒测试、白盒测试与灰盒测试方法
- 《电子元器件的可靠性》——2.5节威布尔分布及其概率纸的结构和用法
- 解决QQ小游戏、微信小游戏 getLaunchOptionsSync()获取的数据为旧数据
- 经典数学问题——三门问题(数据分析面试题)
- tmall.item.add.simpleschema.get( 天猫发布商品规则获取 )