Java并发编程的艺术_Java并发机制的底层实现原理(读书笔记)

1、CPU 术语定义

  • 内存屏障(memory barriers):一组处理器指令,用于实现对内存操作的顺序限制。
  • 缓冲行(cache line):缓存中可以分配的最小存储单位。
  • 原子操作(atomic operations):不可中断的一个或一系列操作。
  • 当处理器识别到从内存中读取的操作数是可缓存的,处理器便读取整个缓冲行到适当缓存(L1、L2、L3...)。
  • 缓存命中:如果进行缓存的内存位置仍是下次处理器访问的地址时,处理器从缓存中读取,而不是内存。
  • 写命中:要写的数据在缓存中,则直接写到缓存,而不是内存。

2、volatile 保证可见性的原理

volatile 修饰的变量在进行写操作时,要执行的汇编指令会有一句 lock 开头的指令,lock 前缀的指令在多核处理器下会引发两件事:

  • 将当前处理器缓存行的数据写回到系统内存,这个过程会锁定总线,或者,锁定对应的缓存。
  • 该写回内存的操作会使其他CPU缓存了该内存地址的数据无效(缓存一致性协议),下次用到相关数据时,会强强制缓冲行填充。

3、volatile 的使用优化

并发包里面的队列集合类:LinkedTransferQueue ,在使用 volatile 时,用一种追加字节的方式优化队列的出队和入队性能。

原理:

  • 一些处理器的 L1、L2、L3 缓存是 64 字节宽,并且不支持部分填充缓存行,当队列的头结点和尾节点不足 64 字节宽时,会被放在同一缓冲行,当某一处理器对头结点处理时,根据缓存一致性原理,会锁定整个缓冲行,导致其他处理器不能对尾节点进行操作,严重影响入队、出队效率。

4、synchronized 实现原理

JVM 基于进入、退出 monitor 对象来实现方法和代码块的同步,两者实现细节不同:

  • 代码块同步:使用 monitorenter、monitorexit 指令实现
  • 方法同步:JVM 规范里没说,估计也差不多

monitorenter 在编译后插入到同步代码块开始的位置,monitorexit 插入到方法结束和异常处。

5、锁的升级

锁的四种状态:

  • 无锁
  • 偏向锁:当一个线程访问同步块并获取锁时,会在对象头和栈帧中的锁记录里面存储线程ID,以后线程进入、退出同步块时不需要加锁、解锁,只需要检查一下对象头的 Mark Word 是否存在该线程的ID。如果测试成功,则获得锁;失败,则再测试一下 Mark Word 中的偏向锁标识是否设置为1(表示当前是偏向锁),没有设置,则使用 CAS 竞争锁,如果设置了,则尝试使用 CAS 将对象头中的偏向锁指向当前线程。
    • 偏向锁的撤销:偏向锁等到竞争出现才会释放锁。
    • JAVA6、JAVA7默认应用程序启动几秒钟后启动偏向锁,可以设置参数,马上启动偏向锁。同时,也可以关闭偏向锁。
  • 轻量级锁
  • 重量级锁

6、原子操作的实现原理

1、总线锁

2、缓存锁

不使用缓存锁的情况:

  • 处理器不支持缓存锁
  • 操作的数据不能缓存
  • 操作的数据跨缓存行

7、JAVA 实现原子操作

1、循环 CAS

  • CAS 的三大问题
    • ABA 问题
    • 循环时间长,开销大
    • 只能保证一个共享变量的原子操作

2、锁机制

发布了42 篇原创文章 · 获赞 2 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/sanmao123456_/article/details/84256017