并发编程的艺术——第二章Java并发机制的底层实现原理

第一节 volatile的应用

定义:Java编程语言允许线程访问共享变量,为了确保共享变量能被准确和一致地更新,线程应该确保通过排他锁单独获得这个变量。

为了提高处理速度,处理器不直接和内存进行通信,而是先将系统内存的数据读到内部缓存后再进行操作

在多处理器下, 为了保证各个处理器的缓存是一致的,就会实现缓存一致性协议,每个处理器通过嗅探在总线上的传播数据来检查自己缓存的值是不是过期了,当处理器发现自己的缓存行对应的内存地址呗修改,就会将当前处理器的缓存设置为无效状态,当处理器对这个数据进行修改操作时,会重新从系统内存中把数据督导处理器缓存里。

volatile两条实现原则:

  1. Lock前缀指令会引起处理器缓存写回到内存
  2. 一个处理器的缓存写回到内存会导致其他处理器缓存无效

第二节synchronized的实现原理与应用

synchronized也称为重量级锁,锁的三种形式:

  1. 对于普通同步方法,锁是当前实例对象
  2. 对于静态同步方法,锁是当前类的Class对象
  3. 对于同步代码块,锁是synchronized括号里面配置的对象

代码块同步时使用monitorenter和monitorexit指令实现的,monitorenter指令是在编译后插入到同步代码块的开始位置,而monitorexit是插入到方法结束处和异常处,JVM要保证每个monitorenter必须有monitorexit与之对应。任何对象都有一个monitor与之关联,当且仅当一个monitor被持有后,它将处于锁定状态。

一Java对象头

synchronized使用的锁是存在Java对象头里的。

Java对象头里的Mark Word里默认存储对象的HashCode,分代年龄和锁标记位。

二锁的升级与对比

JavaSE1.6将锁分为4种状态,级别从低到高:无锁状态,偏向锁状态,轻量级锁状态,重量级锁状态。锁只能升级,不能降级。

偏向锁:(为了让线程获得锁的代价更低)当一个线程访问你同步代码块时,首先检查对象头中是否存储了该线程的ID,如果没有使用CAS替换Mark Word,测试对象头中Mark Word中的线程ID是否执行自己,如果只想自己则执行同步体

轻量级锁:在线程执行代码块之前,JVM会分配空间并将对象头中的Mark Word复制到栈帧中,然后线程尝试使用CAS将对象头中的Mark Word替换为执行锁记录的指针,如果成功,将Mark Word替换为轻量级锁,并执行方法体;如果失败,则当前线程尝试使用自旋来获得锁,如果还是获取失败,锁将膨胀为重量级锁。在轻量级锁解锁时,会使用原子的CAS操作将栈中的信息替换回到对象头。

三原子操作的实现原理

处理器实现原子操作的方式:

  1. 通过总线锁保证原子性
  2. 通过缓存锁定保证原子性

CAS实现原子操作的三大问题:

  1. ABA 问题(使用compareAndSet方法)
  2. 循环时间长,开销大
  3. 只能保证一个共享变量的原子操作

猜你喜欢

转载自blog.csdn.net/wo8vqj68/article/details/82911139
今日推荐