- 🎥 个人主页:Dikz12
- 📕格言:那些在暗处执拗生长的花,终有一日会馥郁传香
- 欢迎大家👍点赞✍评论⭐收藏
目录
常见锁策略
乐观锁和悲观锁
这是"锁的一种特性",并不是一把具体的锁!乐观,悲观是对后续锁冲突是否激烈给出的预测.
乐观锁: 如果预测接下来的锁冲突的概率不大,就可以少做一些事情,就称为"乐观锁".
悲观锁: 如果预测接下来的锁冲突的概率很大,就要多做一些事情,就称为"悲观锁".
轻量级锁和重量级锁
轻量级锁: 锁的开销比较小.
重量级锁: 锁的开销比较大.
乐观锁,通常是可以看作轻量级锁 悲观锁,通常可以看作重量级锁.(也是存在特殊情况的)
一个是预测锁冲突的概率,一个是实际消耗的开销.
自旋锁和挂起等待锁
自旋锁,就是一种轻量级锁的典型实现.
比如: 使用一个while循环,不停的检查当前锁是否释放,如果没有释放,就继续循环,释放了获取锁,从而结束循环.(忙等,消耗cpu换来了更快的响应速度).
挂起等待锁: 就属于重量级锁的一种典型实现.
要借助系统api来实现,一旦出现锁竞争,就会在内核中触发一系列的动作.(比如让这个线程进入阻塞状态,暂时不参与cpu调度).
读写锁
这里的读写锁跟前面"事务"里的给读加锁 和 给写加锁 不是一样的概念.
速写锁,是加锁操作,分成读锁 和写锁.
两个线程加锁过程中:
1: 读锁和读锁之间,不会产生竞争.
2.读锁和写锁之间,会有竞争.(线程安全问题)
3.写锁和写锁之间,也会有竞争.
公平锁和非公平锁
CAS
Compare and swap 简称 CAS. 比较交换的是 内存 和 寄存器.
CAS,就是一个cpu指令,单个的cpu指令,是原子的!就可以使用CAS完成一些操作,进一步替代"加锁".
也就是基于CAS实现线程安全的方式,也称为"无锁编程".
优点:保证了线程安全,同时避免了阻塞.
缺点: 1.代码比较复杂,不好理解
2.适合于特定的场景,不如加锁方式更普适.
原子类里面是基于CAS来实现的!!!
CAS是通过重试的方式,避免穿插; 加锁则是通过阻塞的方式,避免穿插.
ABA问题
CAS 进行操作的关键,是通过值"有没有发生变化" 来作为"没有其它线程穿插执行"的判断依据.
在极端情况下,也是会出现问题的.比如: 把值从A- > B ->A,针对第一个线程来说,看起来好像是这个值没变,实际上已经被穿插执行了.
解决上诉问题:
只要让判定的数值,按照一个方向增长即可. (有增有减,就可能出现ABA)
针对像账户余额这样的操作:
可以引入一个额外的变量,版本号,约定每次修改余额,都要让版本号自增.
在使用CAS的时候,就不是直接判定余额,而是判定版本号.
synchronized原理
synchronozed,属于哪种锁?
1.对于"乐观悲观"是自适应的.
2.对于"轻量和重量"是自适应的.
3.对于"自旋和挂起等待"是自适应的.
4.不是读写锁.
5.是可重入锁 .
6.是非公平锁
锁升级
设计JVM的大佬就把synchronized 设置成了 无锁 偏向锁 轻量级锁 重量级锁 这四种状态.
当你加锁的时候会先进入偏向锁的状态,如果出现"锁竞争"的可能性就立刻升级成轻量级锁, 也就是真正意义上的加锁.
偏向锁:
锁消除
锁粗化
synchronized 里头,代码越多,就认为锁的粒度越粗;代码越少,锁的粒度越细.
粒度细的时候,能够并发执行的逻辑更多,更有利于充分利用多核cpu资源.
但是,如果粒度细的锁,被反复加锁,解锁可能实际效果还不如粒度粗的锁. (涉及到锁竞争问题)