第2章 Java并发机制的底层实现原理

第2章 Java并发机制的底层实现原理

Java中所使用的并发机制依赖于JVM的实现和CPU的指令。

2.1 volatile 的应用
2.1.1 volatile的定义和优化

volatile是轻量级的sychronized, 保证了共享变量的可见性,如果一个字段被声明成volatile,Java线程内存
模型确保所有线程看到这个变量的值是一致的

实现原理: 有volatile变量修饰符的共享变量进行写操作的时候会多出一个lock前缀的指令
lock前缀的指令在多核处理器中引发两件事情

(1)将当前处理器缓存行的数据写回内存

(2)这个写回内存的操作会使在其他CPU里缓存了该内存地址的数据无效

2.1.2 volatile的使用优化

处理器的Cache的高速缓存行是64字节,不支持部分填充缓存行。通过追加到64字节的方式来填满高速缓冲区的缓存行,避免头结点和尾节点加载到同一个缓存行,使头、尾节点在修改时不会互相锁定。

并不是所有使用volatile变量的时候都要追加到64字节

  1. 缓存行非64字节宽
  2. 共享变量不会被频繁读写
2.2 synchronized的实现原理与应用

重量级锁, Java SE 1.6对synchronized进行了各种优化之后,有些情况下它就并不那么重了。

Sychronized锁有三种形式:
·对于普通同步方法,锁是当前实例对象。
·对于静态同步方法,锁是当前类的Class对象。
·对于同步方法块,锁是Synchonized括号里配置的对象

代码块同步是使用monitorenter和monitorexit指令实现的,每个对象有一个monitor,当且一个monitor被持有后,它将处于锁定状态。线程执行到monitorenter指令时,将会尝试获取对象所对应的monitor的所有权,即尝试获得对象的锁

2.2.1 Java 对象头

非数组: 两字宽
数组:三字宽

对象头三种数据:

  • Mark Word(对象hashcode、锁信息等)
  • Class元数据地址
  • 数组长度(数组类型的对象才有),
2.2.2 锁的升级与对比

Java SE 1.6中,锁一共有4种状态,级别从低到高依次是:无锁状态、偏向锁状态、轻量级锁状态和重量级锁状态,这几个状态会随着竞争情况逐渐升级。

在这里插入图片描述

1. 偏向锁
大多数情况下,锁不仅不存在多线程竞争,而且总是由同一线程多次获得,为了让线程获得锁的代价更低而引入了偏向锁。

对象头:记录获得锁的线程ID, 同步块时不需要进行CAS操作来加锁和解锁,只需简单地测试一下对象头的Mark Word里是否存储着指向当前线程的偏向锁。如果测试成功,表示线程已经获得了锁。如果测试失败,则需要再测试一下Mark Word中偏向锁的标识是否设置成1(表示当前是偏向锁):如果没有设置,则
使用CAS竞争锁;如果设置了,则尝试使用CAS将对象头的偏向锁指向当前线程。

(1)偏向锁的撤销: 偏向锁使用了一种等到竞争出现才释放锁的机制,所以当其他线程尝试竞争偏向锁时,持有偏向锁的线程才会释放锁。偏向锁的撤销,需要等待全局安全点(在这个时间上没有正在执行的字节码)。
(2) 取消轻量级锁:设置参数JVM参数关闭偏向锁:-XX:-
UseBiasedLocking=false

2. 轻量级锁

(1)轻量级锁加锁:
在线程栈帧中创建用于存储锁记录的空间,并将对象头中的Mark Word复制到锁记录中,官方称为Displaced Mark Word。然后线程尝试使用CAS将对象头中的Mark Word替换为指向锁记录的指针。如果成功,当前线程获得锁,如果失败,表示其他线程竞争锁,当前线程便尝试使用自旋来获取锁。

(2)轻量级锁解锁
轻量级解锁时,会使用原子的CAS操作将Displaced Mark Word替换回到对象头,如果成功,则表示没有竞争发生。如果失败,表示当前锁存在竞争,锁就会膨胀成重量级锁。

3. 锁的优缺点对比
在这里插入图片描述

2.3 原子操作的实现原理

1. 术语定义:

2. 处理器如何实现原子操作
(1)总线锁定:总线锁就是使用处理器提供的一个LOCK#信号,当一个处理器在总线上输出此信号时,其他处理器的请求将被阻塞住,那么该处理器可以独占共享内存
(2)使用缓存锁保证原子性
缓存一致性机制会阻止同时修改由两个以上处理器缓存的内存区域数据,当其他处理器回写已被锁定的缓存行的数据时,会是缓存行无效

3. Java如何实现原子操作
(1)使用循环CAS实现原子操作
在Java中可以通过锁和循环CAS的方式来实现原子操作
(2)CAS实现原子操作的三大问题

1)ABA问题,ABA问题的解决思路就是使用版本号。在变量前面
追加上版本号,每次变量更新的时候把版本号加1,那么A→B→A就会变成1A→2B→3A

2)循环时间长开销大。自旋CAS如果长时间不成功,会给CPU带来非常大的执行开销。

3)只能保证一个共享变量的原子操作

(3)使用锁机制实现原子操作
锁机制保证了只有获得锁的线程才能够操作锁定的内存区域

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值