java并发编程(一):volatile和锁的级别

1,volatile的运用;

volatile是个轻量级的synchronized,在多线程中保证了共享变量的‘可见性’,就是一个线程修改共享变量的值之后,另一 程能够读到这个修改的值;

volatile修饰的变量变成汇编之后会有一lock前缀指令;

volatile的两条实现原则:

(1),Lock前缀指令会引起处理器缓存会回写到内存;

(2),一个处理器的缓存回写到内存中,会导致其他处理器的缓存失效。(当这个地址是共享状态的时候,一个处理器修改这个地址上的值,其他处理器会嗅探这个处理器打算写的内存地址,如果刚好是共享的状态的地址,那么会强制执行缓存行填充)

2,synchronized的原理与运用;

jvm基于进入和退出monitor对象来实现方法同步和代码块的同步, monitorenter,monitorexit;

monitorenter指令是在编译后插入到同步代码块的开始位置,monitorexit是插入到方法结束处,或者是异常处;

2.1,java对象头;

扫描二维码关注公众号,回复: 2436851 查看本文章

对象包含:对象头,对象实体,补充位置

对象头包含:MarkWord,Class Metadata Address,array length

MarkWord:存储对象的hashcode或锁信息,分带年龄;

ClassMatadata Address:存储到对象类型数据的指针

Array length:数组的长度(如果当前对象为数组对象)

2.2,锁的升级

无锁状态 ,偏向锁,轻量级锁,重量级锁;级别依次递增,只能升级,不能降级;

(1)偏向锁(在锁不存在多线程竞争,并且经常是同一个线程获取锁)

当一个线程访问同步块并获取锁的时候,会在对象头的栈帧中的锁记录位置记录偏向锁的线程id,以后该线程再进入或者退出同步块的时候不需要通过CAS来进行加锁和解锁操作;

锁的释放是在出现竞争才释放锁;

(2)轻量级锁(不会出现阻塞现象,但是会消耗CPU,竞争激烈可能升级为重量级锁,反应速度比重量级锁还慢)

加锁:线程再执行同步块的时候,jvm会现在栈帧中开辟一个存储锁记录的空间,并将对象头中的Markword 复制到锁记录中,然后线程会使用CAS将对象头中的MarkWord替换为指向锁记录的指针,如果成功,线程获取锁,如果失败,则表示其他线程占有该锁,当前线程会通过自旋(等一等,待会再来尝试一次)来获取线程;

解锁:解锁时候会通过cas将栈帧中复制的Markword 替换回到对象头中,如果成功,表示没有竞争,失败表示存在竞争关系,锁膨胀成重量级锁;

(3)重量级锁;(直接进行阻塞,响应时间慢)

一旦进入重量级锁,就不能变为轻量级锁了,获取锁的线程如果获取失败,就会被阻塞,当持有线程的锁释放锁之后才会被唤醒,唤醒的锁要通过竞争才能获取对象的锁;

2.3,原子操作

原子:不能被进一步分阶段的粒子,原子操作就是就是中途不能中断的操作;

CAS:compare and swap;输入一个旧值和一个新值,操作期间比较旧值是否发生了改变,如果没发生改变就把新值设置进去,如果发生改变之后,就不进行变化操作;

ABA问题:一个值原来为A,变为了B,又变为了A,CAS检查时候发现没有变化,但是实际上是发生了变化,JDK 中添加了AtomicStampedReferance,其中包含了版本号,一个预期引用和预期值,当都相同的时候才开始更新值。

猜你喜欢

转载自blog.csdn.net/waterflying2015/article/details/80835560
今日推荐