#Java并发机制的底层实现原理
Java并发机制的底层实现原理
volatile的应用
volatile是轻量级的synchronized,不会引起线程上下文的切换和调度。
volatile的定义与实现原理
如果一个字段被声明为volatile,java线程内存模型确保所有线程看到的变量值是一致的。
volatile的两条实现原则:
1.Lock前缀指令会引起处理器缓存写回到内存。
2.一个处理器的缓存写回到内存会引起其他处理器的缓存失效。
synchronize的的实现原理与应用
java的每一个对象都可以作为锁,具体表现为3种形式:
1.普通同步方法,锁是当前实例。
2.静态同步方法,锁是当前类的class对象。
3.同步方法块,锁是synchronized括号里配置的对象。
java对象头
synchronize的用的锁是存储在java对象头中的。
1.Mark Word:存储对象的hashcode或锁信息等。
2.Class Metadata Address:存储到对象类型数据的指针。
3.Array Length:数组的长度(如果当前对象是数组)。
锁的升级与对比
Java中锁一共有四种,级别从高到低依次是:无锁状态、偏向锁状态、轻量级锁状态和重量级锁状态。锁可以随着竞争的激烈升级,但是不能降级。
偏向锁
如果一个线程访问一个同步块并获取锁,会在对象头记录锁偏向的线程ID,以后线程在获取锁时,只需要检查对象头是否记录该线程的ID;如果是,则获取成功,否则,检查对象头Mark word中偏向锁标识位是否为1,如果不是1,则通过CAS竞争锁,如果是1,则尝试让锁偏向当前线程(判断ThreadId是否为0,是的话获取成功,否则,撤销轻量级锁)。
(1)偏向锁的撤销
偏向锁是一种等到竞争出现才释放锁的机制,偏向锁的撤销需要等到全局安全点(在这个时间点上没有执行的字节码)。
首先暂停拥有偏向锁的线程,然后检查该线程是否存活,如果没有存活,则将对象头设置为无锁状态(是否支持重偏向,不支持的话膨胀为轻量级锁,进行CAS竞争),否则,拥有偏向锁的栈会被执行(此时,偏向锁膨胀为轻量级锁),遍历对象的锁记录,栈中的锁记录和对象头的Mark Word要么重新偏向于其他线程,要么恢复到无锁状态或者标记该对象不适合作为偏向锁,最后唤醒暂停的线程。
(2)关闭偏向锁
JVM设置-XX:-UseBiasedLocking=false。
轻量级锁
(1)轻量级锁的加锁
线程在执行同步块时,JVM会先在当前线程的栈桢中创建用于存储锁记录的空间,并将对象头中的Mark Word复制到锁记录中(Displaced Mark Word)。然后线程尝试使用CAS将对象头中的Mark Word替换为指向锁记录的指针,该过程通过自旋实现。
(2)轻量级锁的解锁
解锁时,会使用原子的CAS将Displaced Mark Word替换回到对象头,如果成功,则表示无竞争。否则,有竞争锁膨胀为重量级锁。
优缺点比较
(1)偏向锁
优点:加锁与解锁无消耗,速度快。
缺点:锁竞争导致锁撤销带来额外开销。
使用场景:单个线程访问。
(2)轻量级锁
优点:竞争的线程不会阻塞,提高了程序的响应速度。
缺点:自旋会消耗CPU。
使用场景:追求响应时间。
(3)重量级锁
优点:不使用自旋,无CPU消耗。
缺点:线程阻塞,响应时间长。
使用场景:追求吞吐量。
原子操作的实现原理
处理器实现原子操作
(1)总线锁定
锁定CPU和内存之间的通信,使得处理器独占内存。
(2)缓存锁定
内存区域如果被缓存在了处理器的缓存中,并且在Lock操作期间被锁定,那么当执行锁操作回写到内存时,处理器修改内部的内存地址,并允许它的缓存一致性机制来保障操作的原子性,因为缓存一致性机制会阻止由两个以上的处理器修改缓存的内存区域数据,当其他处理器回写到已锁定的缓存时,会使缓存失效。
Java如何实现原子操作
使用循环CAS实现原子操作
CAS会引入三个问题:
(1)ABA问题
如果一个值原来是A,变成了B,又变成了A,通过CAS检查发现值未变,但实际上变了。解决方法:使用版本号。
(2)循环时间长导致开销大
(3)只能保证一个共享变量的原子操作