锁分析上

一、syncronized

原理:

Jvm基于进入和退出Monitor对象来实现方法同步和代码块同步。
代码块同步是使用 monitorenter和monitorexit指令实现,monitorenter指令是在编译后插入到同步代码块的开始位置,而monitorexit是插入到方法结束处和异常处,任何对象都有一个monitor与之关联,当且一个monitor被持有后,它将处于锁定状态。

1.JPG

锁升级步骤

使用syncronized后,其他线程想要进入此区块,内部进化过程

  1. 无锁01:1位存储是否是偏向锁,0是无锁状态、1是偏向锁状态
  2. 偏向锁实现01: java对象头mark word存储锁偏向的线程id,一段代码一直被某个线程访问, 置换threadid时候只需要一次CAS操作
  3. 轻量级锁实现00:锁是偏向锁时候,并正在被其他线程访问,线程想要访问时候,需借助CAS+有限次数自旋判断,避免线程切换cpu消耗
  4. 重量级锁实现10:当通过偏向锁没有获取到,则此线程进入阻塞状态,释放cpu

字节占用分配

64位虚拟机

  1. 无锁: 25位unused、31位hashcode、1位cms_free、4位对象分代年龄、1位是否是偏向锁、2位锁标志位
  2. 偏向锁:54位偏向锁的线程id、2位Epoch、1位Cms_free、4位对象分代年龄、1位偏向锁标志、2位锁标志位
  3. 轻量级锁: 62位指向栈中锁的记录的指针、2位锁标志位
  4. 重量级锁:62位指向重量级锁的指针、2位锁标志位

2.JPG

32位虚拟机:

  1. 无锁:25位对象存储hashcode、4位对象分代年龄、1位是否偏向锁、2位锁标志位
  2. 偏向锁:23位存储线程id、2位epoch、4位存储对象分代年龄、1位是否是偏向锁、2位锁标志位
  3. 轻量级锁:30位指向栈中的锁记录指针、2位存放锁标志位
  4. 重量级锁:30位指向重量级锁的指针、2位锁标志位

3.JPG

二、CAS

原理:

内存位置、预期原值、新值

如果内存位置的值与预期原值相匹配,那么处理器会自动将该位置的值更新为新值,否则不做任何处理。

借助于cpu的原子指令

Cpu的原子指令操作依赖于两种技术

1、总线锁定:处理器提供LOCK信号,当一个处理器在总线上输出此信号后,其他处理器的请求将被阻塞,该处理器就会独占共享内存(把cpu和内存之间的通信锁住)

2、缓存锁定:缓存一致性阻止同时修改两个以上处理器缓存的内存区域数据,当其他处理器回写已被锁定的缓存行的数据时,会使缓存行无效

4.JPG
内部依赖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

5.JPG

新建状态New是 new Thread之后

就绪状态Runnable是执行start方法之后

运行状态Running是cpu调度处于就绪状态

阻塞状态

blocked—暂时放弃对cpu的使用权

1、 sleep join或者io读写

2、 synchronized同步阻塞

3、 wait 等待阻塞

注意:sleep会让出cpu,但是不会让出锁,wait会让出锁和cpu资源

死亡状态dead- 正常退出或者异常退出

四、参考链接:

blog.csdn.net/liangwenmai…

blog.nowcoder.net/n/3f413b4af…

www.cnblogs.com/xiaolincodi…

elsef.com/2020/03/08/…

xie.infoq.cn/article/2b4…

线程状态分析:blog.csdn.net/wm5920/arti…

美团锁实践:
tech.meituan.com/2018/11/15/…

Syncronized知乎解释: zhuanlan.zhihu.com/p/29866981

juejin.cn/post/684490…

segmentfault.com/a/119000002…

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享