//解释:
//锁策略就是在 加锁过程中,处理冲突的方式
//锁策略不只是Java独有的,它适用于 线程加锁问题 而不是 哪种语言特有的
一、乐观锁 和 悲观锁
乐观锁
//加锁前 预估出现锁冲突 的概率不大, 在加琐时 就不会做那么多的准备
!!!加锁过程做的事越少,加锁速度就越快,会引起其他问题(可能会消耗更多CPU资源//后面红色括号位置会解释)
悲观锁
//加锁前 预估出现锁冲突 的概率大, 在加琐时 会做更充分的准备
!!!准备的越充分 加锁速度就越慢 出现问题的几率就越小
二、轻量级锁 和 重量级锁
轻量级锁
//加锁开销小,速度快=> 轻量级锁,一般就是乐观锁
重量级锁
//加锁开销大、速度慢=>重量级锁,一般就是悲观锁
!!!其实这两种锁就是乐观锁和悲观锁的延申
- 乐观锁和悲观锁是在加锁前对冲突的预估
- 轻量级锁和重量级锁是在对加锁后的结果评估
- 无非就是两种不同的角度对同一件事的 评价
三、自旋锁 和 挂起等待锁
自旋锁
//自旋锁是 对 轻量级锁的一种实现(轻量级锁又是乐观锁的延申-->自旋锁也乐观),对加锁的成功率预期高
搭配while循环 反复、快速的不断加锁,直到加锁成功、自然循环到结束
就算加锁失败仍然不放弃、不阻塞 而是继续循环尝试加锁
小栗子:{
你的女神有一个男朋友(处于加锁状态)
你是女神的一个舔狗,每天反复快速的对女神献殷勤(尝试加锁)
一旦女神分手(释放锁)
这时候英俊潇洒的你,趁虚而入(加上锁了)
}
以上只是一只哥布林的幻想,下面才是各位添狗的真实情况:
{
女神身边有很多的舔狗(加锁冲突,很多的线程在尝试对这个对象加锁)
在也在乐观的反复快速的献殷勤(尝试加锁)
你成功上位的几率存在但是不大(舔狗)
但你乐此不疲(自旋锁)
}
总结:
- 自旋锁 对加锁的冲突概率不大 ,即使没成功也会反复快速的尝试加锁
- 反复这个过程,其实就是在消耗cpu资源,即使预期加锁冲突概率小,一旦很久都没加上
- 就会消耗大量的CPU资源(解释了乐观锁为什么可能会消耗大量cpu资源)
- 如果加锁的线程特别多,自旋锁的意义就不大,上位太难了.
挂起等待锁
与上面情况相反
表白被拒,悲痛欲绝,以后再也不联系了(进入阻塞等待)
可以后真的不联系了吗?一旦女神分手了,舔狗上线(又继续尝试对女神进行加锁)
可是这样存在一个问题,自旋锁是即使女神没分手,也每天快速反复的对女神献殷勤
这样他就能第一时间尝试对女神加锁(快速对对象加锁)
而挂起等待锁是听别人说的,这个消息可能是很久之前的
这时候再去尝试加锁的话,成功率就不大了
总结:
- 一旦尝试加锁失败就进入阻塞
- 但多久唤醒需要看cpu对线程重新调度
- 坏处是不知道何时才能重新调度
- 好处是阻塞期间,CPU资源被释放,可以借此做其他的任务
几个小问题:
既然 自旋锁和挂起等待锁 与 轻量级锁和重量级锁 两两对应
那么如何解释这一对应情况
挂起等待锁是重量级锁的实现?
- //上面说过重量级锁,开销大,速度慢
- //而线程一旦进入阻塞,就需要重新调度 这时候就需要消耗CPU资源
- //而调度又是个复杂繁琐的过程 速度自然就慢了
又如何体现他是一种悲观锁?
- //尝试加锁失败,进入阻塞
- 适用于锁冲突激烈的情况 对应了 对锁冲突概率大的特性
四、普通互斥锁 和 读写锁
普通互斥锁
//类似synchronized 的加锁解锁规则
读写锁分为
//1.加读锁
//2.加写锁
读锁和读锁之间 不会锁冲突(阻塞)
读锁和写锁之间,写锁和写锁之间 会发生锁冲突(阻塞)
读写锁优势就在于当线程需要读取大量数据的时候,不会发生互斥、锁冲突,提升了效率
五、 公平锁 和 非公平锁
!!!这里的公平是基于时间上的先来后到原则
公平锁
//先来的线程先加锁,先来后到
非公平锁
//不遵循先来后到原则
- 公平锁的实现需要引入优先级队列记录线程先后顺序
- 使用公平锁可以解决线程饿死问题
- //线程饿死 简单来说就是一个线程对一个锁对象疯狂的加锁解锁再加锁 ,导致其他线程加不上锁
六、可重入锁 和 不可重入锁
可重入锁
//同一线程对一个锁对象,多次加锁不会死锁,就是可重入锁
!!!synchronized 是可重入锁
可重入锁不代表不会产生死锁,还得看用户代码咋写的
不可重入锁
//反之,如果同一线程对同一锁对象多次加锁,死锁了就是不可重入锁
注:像我这种新手只需要知道个大概就行,这些东西:面试会考,工作用不上
知道咋用synchronized就行