java并发编程的艺术

术语 描述
memory barries(内存屏障) 处理器指定,实现对内存操作的顺序限定
cache line(缓冲行) 缓存中的最小存储单位,需要使用多个主内存周期(存储周期?)
atomic operations(原子操作) 不可中断的操作
cache line fill (缓存行填充) 处理器读取整个缓存行到适当的缓存
cache hit(缓存命中) 处理器操作地址是缓存行填充的内存地址
write hit(写命中) 写回数据时,如果内存地址在缓存行中,写命中(直接操作缓存行)。

volatile

  • volatile提供了比synchronized的使用和成本低,不会引起上下文切换和调用
  • (原理)转汇编时会多出一行lock汇编代码
    • 将当前缓存行写回内存系统
    • 写回操作使其他cpu的缓存无效

ps: 缓存一致性协议,每个处理器通过嗅探在总线上传播的数据检查缓存是否过期。发现缓存行的内存被修改后,将缓存行置为无效状态,当处理器对数据修改操作时,把数据从内存读取到处理器缓存。

Lock的处理器原理

缓存回写内存

锁缓存、写回内存、使用缓存一致性确保修改原子性。缓存一致性会组织同时修改由两个以上处理器缓存的内存区域数据。

一个处理器的缓存回写内存会导致其他处理器的相同缓存失效

处理器使用嗅探技术保证它的内部缓存、系统内存和其他处理器的缓存在总线上保持一致。

volatile使用优化

  • 追加字节填满缓冲行(Linked-TransferQueue),大多处理器告诉缓存行是64字节。试图修改缓存行数据时,会锁定缓存行。导致其他处理器不能访问锁定的节点。
  • 什么时候不能追加到64字节
    • 缓存行为32字节的cpu时
    • 共享变量不会被频繁写入
      ps:java7会淘汰或重新排列无用字段,不能单独使用Object对象来填充

synchronized

  • 曾经很重,1.6优化(引入偏向锁和轻量级锁)后,变轻。
  • 对于普通方法,锁是当前实例对象
  • 静态同步方法,锁是当前类的class对象
  • 对于同步方法块(synchonized)

在哪里上锁

synchronized的锁是存在java对象头里面。

ps:对象头:数组类型用三个字宽(多出的一个存放array length);非数组类型,用两个字宽(字宽:Word,32位机1字宽为4字节)

q1:64位虚拟机,在轻量级锁、重量级锁时,mark word的结构?

对象头的mark word

Mark Word默认存储对象的HashCode、分代年龄、锁标记位。但是会随着锁标志位的变化而变化。

锁升级与对比

1.6为了减少获得锁和释放锁的 带来的性能消耗,引入了偏向锁和轻量级锁。因此共有四种锁状态(如下),同时锁只能升级,无法降级。

  • 无锁状态
  • 偏向锁状态
  • 轻量级锁状态
  • 重量级锁状态

偏向锁

  • 偏向锁撤销:
    1. 持有锁的线程不会在同步代码块执行结束后,释放锁。
    2. 新线程竞争锁-->暂停持有偏向锁的线程-->检查状态
            --live-->恢复无锁、升级偏向锁--  恢复持有 偏向锁的线程
            --dead-->置为无锁          --/
  • 关闭偏向锁:
启动几秒后才会激活,关闭延迟:-XX:BiasedLockingStartupDelay=0
如果锁通常处于竞争状态时,可以关闭偏向锁:-XX:-UseBiasedLocking=false

轻量级锁

  • 加锁
    1.  将对象头中的mark word复制到锁记录(当前线程的栈帧)中。
    2. CAS将mark word替换为指向锁记录的指针
    3. 成功则获取锁,失败则尝试自旋获取锁
  • 解锁
   1. CAS将Displaced Mark Word替换回到对象头。
   2. 成功则表示无竞争发生;失败则表示存在竞争,膨胀为重量锁。

重量级锁

重量级锁会阻塞获取锁的线程,当持有锁的线程被释放后,才会唤醒这些线程。

原子操作实现原理

cpu的实现

总线锁定

  • 使用处理器提供的LOCK #信号
  • 一个处理器在总线上输出此信号时,其他处理器的请求将会被阻塞住,该处理器可以独占共享内存
    ps:总线锁,锁定的是CPU和内存间的通信。

缓存锁

  • 不用声明LOCK #信号
  • 使用缓存一致性机制,来保证操作的原子性

两种情况处理器不会使用缓存锁定

  • 数据不能被缓存在处理器内部,操作的数据跨多个缓存行时。使用总线锁定
  • 某些处理器不支持缓存锁定。

ps:针对以上机制,通过Intel处理器提供的Lock前缀的指令来实现(位测试和修改指令:BTS、BTR、BTC;交换指令:XADD、CMPXCHG;其他操作指令:ADD、OR)

java的实现

思路:锁和循环CAS

CAS
  • CAS利用了处理器提供的CMPXCHG指令实现的。
  • 一些问题(ABA、循环时间长开销大、只能保证一个共享变量的操作)
使用锁机制实现原子操作

ps:除了偏向锁,JVM实现锁的方式都采用了循环CAS。

原文地址:https://www.cnblogs.com/enhe/p/12141680.html