1.volatile 的应用
volatile 是轻量级的synchronized,保证了共享变量的“可见性”。它比所以你synchronized运行成本低。“可见性”值的一个线程在修改变量时,,另一个线程可以读到这个值。而且它不会引起上下文的切换和调度。
1.1volatile 定义与原理
volatile 允许线程访问共享变量。保证java线程内存模型和所有线程所看到的变量值是一致的
1.2 cup术语定义
术语 英文单词 术语描述
内存屏障 memory 一组处理器的指令,用于实现对内存操作的顺序
缓冲行 cache line 最小存储单位,处理器填写缓存行时加载整个缓存行。
原子操作 atomic operations 不可中断的一个或一系列的操作
缓冲行填充 cache line fill 当处理器识别到从内存中读取操作数的缓存,处理器读取整个缓存高速缓存行 到 适 当 的 中 缓存中L1 L2,L3
缓存命中 cache hit 高速缓存行填充操作数的内存位置,下次处理器访问地址,从处理器缓存中 读 取 而 不 是 从 内存中
写命中 write hit 当处理器讲操作数写到内存缓存区域时,它首先检查这个缓存的内存地址是 否在缓存行中,如果存在一个有效的缓冲行则处理器将这个操作数写到 到 缓 存 而不是写回内存中。
写缺失 write misses the cache 一个有效的缓存行被写入到不存在的内存区域
2.volatile 的实现规则
2.1Lock前缀指令会引起处理器缓存回写到内存中。
①以前的Lock指令在信号期间处理器会独占共享内存(这样会导致总线开销很大)
②现在的存储器Lock#信号一般不会锁总线而是锁内存。毕竟总线开销比较大。缓存锁定。保证了缓存一致性机制会阻止
同时修改两个以上处理器缓存的内存区域数据
2.2一个处理器的缓存回写到内存中会导致其他处理器的缓存无效
①IA-32处理器和Inel64处理器使用的MESI(修改,独占,共享,无效),控制协议维护内部缓存和其他的处理器缓存的 一致性
②处理器用嗅探技术保证它的的内部缓存,系统内存和其他处理器的缓存的数据在总线上保持一致
3.volatile的使用优化
3.1利用的并发包中的Linke-TransferQueue ,用追加字节的方式来优化队列出队和入队的性能
①它是使用内部类的类型定义队列的头节点和尾节点,内部类PaddedActomicReference只是做了一件事,共享变量 追加字节。
②处理器不同的情况下,64位字节的宽,队列头节点个尾节点都不足64字节,处理器会它们都读到同一个高数缓存行中,在多处理器会缓存同样的头节点和尾节点。当一个处理器修改头节点的会将整个缓存行锁定。那么在缓存一致性机制的左右你改下,其他处理器不能访问自己高速缓存的尾节点。而队列的入队和出队操作在不停的修改头节点和尾节点,所以追加64字节来填满高速缓冲区的缓存行,避免头节点和为节点加载到同一个缓存瀚,是头,尾节点在修改是不会相互锁定
4.syncronized的实现原理和应用
4.1java中的每一个对象可以作为锁,表现的三种形式
①对于普通同步方法,锁是当前实例对象
②对于静态同步方法,锁是当前类的Class的对象
③对于同步方法块 所示Syncronized括号的配置对象
4.2java对象头
① syncronized用的锁是存在java的对象头里,对象是数组,则虚拟机用的3个字款 ,如果不是用2个字节。
②Mark Word 存储对象的hashCode或锁信息等
③java对象头里面默认存储对象的HashCode,分代年龄和锁标志。
④Mark Word 里存储的数据会随着锁标志位的变化而变化。
⑤Mark Word有4种数据
轻量级,重量级锁,GC标志,偏向锁
5.偏向锁
5.1当一个线程访问同步并获取锁时,会在对象头和栈帧中的锁记录里存储锁偏向的线程id,不需要CAS
操作来加锁和解锁。若果对象头的Mark Word是否存储着指向当前的偏向锁的,如果陈宫,表示线程已经获取到了锁,若果不存在,看是否Mark Word 中是的锁的标志是否设置为1,如果没有,则使用CAS竞争锁 ,如果设置了就是用cas将对象头的偏向锁指向当前线程
5.2偏向锁的撤销
① 偏向锁的使用了 一种等到竞争出现才释放锁的机制。所以当其他线程尝试竞争偏向锁。持有偏向锁的线程会得到释放,偏向锁的撤销。需要等待全局安全点,它会首先暂停拥有的偏向锁的线程,然后检查持有偏向锁的线程是否或者,如果线程不处于活动状态,则将对象头设置为无锁的状态,如果线程还是活着,拥有偏向锁的栈会被指向
5.3偏向锁的获得和撤销流程
6.轻量级锁
6.1 JVM 会先在当前线程栈帧中创建用于存储锁记录空间,并且将对象中的Mark Word复制到记录中,,然后线程尝试使用CAS将对象中的Mark Word替换为指向锁记录的指针,如果成功,当前线程获得锁,若果失败,表示其他线程竞争锁,当前线程便尝试自旋来获取锁。
6.2轻量级锁解锁
轻量级锁在解锁是,会使用原子CAS操作将Mark Word 替换回到对象头,如果成功,测表示没有发生竞争。如果失败。表示当前锁存在竞争,锁就会膨胀成完成重量级锁。
如图所示
6.3锁的优缺点对比
锁 | 优点 | 缺点 | 适用场景 |
偏向锁 | 加锁和解锁不需要外的消耗和执行非同步方法相比仅存在纳秒级的差距 | r如果线程间存在竞争锁,会带来额外的锁撤销的小消耗 | s适用于只有一个线程访问同步块场景 |
轻量锁 | 竞争的线程不会堵塞,提高了程序的响应速度 | 如果始终得不到锁竞争的线程,使用自旋会消耗CPU | 追求响应时间,同步块执行速度非常快 |
重量级锁 | 线程竞争不适用自旋,不会消耗CPU | x线程堵塞,响应时间缓慢 | 追求吞吐量,同步块执行速度较长 |
7.原子操作的实现原理
7.1原子操作就是不可中断的一个或一系列操作。
7.2.处理实现原子操作
①第一个机制是通过总线锁保证原子性;
②第二机制通过缓存锁定来保证原子性;
8.java如何实现原子操作
8.1通过锁和循环CAS的方式来实现原子操作
8.2CAS实现原子的操作三大问题
①ABA问题。用JDK提供的包AtomicStampedReference 解决ABA问题
public Boolean compareAndSet(V expectedReference //预期引用
V newReference //更新后的引用
int exceptedStamp,//预期标志
int newStamp //更新后的标志
)
②循环时间长开销大
③只能保证一个共享变量的原子操作