一、syncronized
原理:
Jvm基于进入和退出Monitor对象来实现方法同步和代码块同步。
代码块同步是使用 monitorenter和monitorexit指令实现,monitorenter指令是在编译后插入到同步代码块的开始位置,而monitorexit是插入到方法结束处和异常处,任何对象都有一个monitor与之关联,当且一个monitor被持有后,它将处于锁定状态。
锁升级步骤
使用syncronized后,其他线程想要进入此区块,内部进化过程
- 无锁01:1位存储是否是偏向锁,0是无锁状态、1是偏向锁状态
- 偏向锁实现01: java对象头mark word存储锁偏向的线程id,一段代码一直被某个线程访问, 置换threadid时候只需要一次CAS操作
- 轻量级锁实现00:锁是偏向锁时候,并正在被其他线程访问,线程想要访问时候,需借助CAS+有限次数自旋判断,避免线程切换cpu消耗
- 重量级锁实现10:当通过偏向锁没有获取到,则此线程进入阻塞状态,释放cpu
字节占用分配
64位虚拟机
- 无锁: 25位unused、31位hashcode、1位cms_free、4位对象分代年龄、1位是否是偏向锁、2位锁标志位
- 偏向锁:54位偏向锁的线程id、2位Epoch、1位Cms_free、4位对象分代年龄、1位偏向锁标志、2位锁标志位
- 轻量级锁: 62位指向栈中锁的记录的指针、2位锁标志位
- 重量级锁:62位指向重量级锁的指针、2位锁标志位
32位虚拟机:
- 无锁:25位对象存储hashcode、4位对象分代年龄、1位是否偏向锁、2位锁标志位
- 偏向锁:23位存储线程id、2位epoch、4位存储对象分代年龄、1位是否是偏向锁、2位锁标志位
- 轻量级锁:30位指向栈中的锁记录指针、2位存放锁标志位
- 重量级锁:30位指向重量级锁的指针、2位锁标志位
二、CAS
原理:
内存位置、预期原值、新值
如果内存位置的值与预期原值相匹配,那么处理器会自动将该位置的值更新为新值,否则不做任何处理。
借助于cpu的原子指令
Cpu的原子指令操作依赖于两种技术
1、总线锁定:处理器提供LOCK信号,当一个处理器在总线上输出此信号后,其他处理器的请求将被阻塞,该处理器就会独占共享内存(把cpu和内存之间的通信锁住)
2、缓存锁定:缓存一致性阻止同时修改两个以上处理器缓存的内存区域数据,当其他处理器回写已被锁定的缓存行的数据时,会使缓存行无效
内部依赖Unsafe.java类实现
对于compareAndSwapInt如何保证原子性
public final boolean compareAndSet(int expect, int update) { return unsafe.compareAndSwapInt(this, valueOffset, expect, update); }
以下为do-while类型,设置不成功则一直自旋
getAndAddInt getAndAddLong getAndSetInt getAndSetLong getAndSetObject
存在问题:
1、ABA问题:
Unsafe.java类中
public final int getAndAddInt(Object o, long offset, int delta) { int v; do { v = getIntVolatile(o, offset); } while (!compareAndSwapInt(o, offset, v, v + delta)); return v; }
ABA问题来源是获取和比较是两步操作,所以就会存在并发的问题
单独拿出来getIntVolatile 和compareAndSwapInt都是原子操作
所以对于只调用下面类似AtomicInteger.java类中这个方法是cpu保证的原子操作是没有问题的
public final boolean compareAndSet(int expect, int update) { return unsafe.compareAndSwapInt(this, valueOffset, expect, update); }
通过引入AtomicStampedReference 类来解决CAS问题,即不但比较内存地址,而且比较数据被修改的版本号
2、循环时间长开销(由于类似getAndAddInt的方法的do while循环导致)
3、只能保证一个共享变量的原子操作
三、附加线程状态
New runnable running blocked dead
新建状态New是 new Thread之后
就绪状态Runnable是执行start方法之后
运行状态Running是cpu调度处于就绪状态
阻塞状态
blocked—暂时放弃对cpu的使用权
1、 sleep join或者io读写
2、 synchronized同步阻塞
3、 wait 等待阻塞
注意:sleep会让出cpu,但是不会让出锁,wait会让出锁和cpu资源
死亡状态dead- 正常退出或者异常退出
四、参考链接:
blog.nowcoder.net/n/3f413b4af…
线程状态分析:blog.csdn.net/wm5920/arti…
美团锁实践:
tech.meituan.com/2018/11/15/…
Syncronized知乎解释: zhuanlan.zhihu.com/p/29866981