Volatile
volatile认为是轻量级的synchronized,使用和执行成本更低,不会引起上下文切换和调度;
volatile修饰的变量写操作时的汇编代码会多一条:lock指令,作用:
1. 将当前处理器缓存行数据写回内存;
2. 一个处理器的缓存回写会使其他缓存了该内存地址的数据无效;
缓存一致性协议:每个处理器通过嗅探总线数据来保证数据一致;
需要多个线程区写同一个共享变量,volatile变量是不合适的
synchronized
实现原理:JVM基于进入和退出monitor对象实现,进入前代码经编译后插入monitorenter指令,退出时插入monitorexit指令;
任何对象都有一个monitor,monitorenter和monitorexit是配对的;
锁信息的存储:存储在Java对象头中的mark word中;
锁有4种状态:无锁、偏向锁、轻量级锁、重量级锁,锁只能升级不能降级,是为了提高获得和释放锁的效率;
偏向锁:
引入目的:通常锁由一个线程多次获得,为了减少这种情况下的获得代价;
实现原理:Java对象头的Mark Wod中存储了偏向锁的线程ID和锁状态,存在竞争时才释放锁或者竞争锁;
加锁:线程进入同步块时,将线程ID记录到对象的头中,下次进入只需要检测头信息即可重新获得,没有信息才使用CAS竞争锁;
解锁:退出同步代码块或者有线程竞争时;
关闭:Java默认开启了偏向锁,如果确定程序大多数都处于竞争,可以通过JVM参数关闭偏向锁,这时会进入轻量级锁;
轻量级锁:
加锁:线程将对象的MarkWord复制到线程的栈空间并修改然后写回对象,失败时通过自旋获取锁;
解锁:写回对象的MarkWord,若失败表示锁存在竞争,锁升级为重量级锁,重量级锁时其他线程都是阻塞
锁对比原子操作
处理器如何实现:
总线锁:处理器操作时锁总线,让其他处理器不能操作;
缓存锁:保证内存的缓存一致(即volatile的缓存一致性);
Java如何实现:锁、CAS(利用处理器的CMPXCHG指令)
CAS的问题及解决:
1. ABA问题;解决:数据加上版本号;Java实现:AtomicStampedReference;
2. 循环时间长开销大;解决:利用处理器的pause指令延迟执行;
3. 只能保证一个共享变量的原子操作;解决:锁或者将多个变量包装到一个变量;Java实现:AtomicReference;
JVM中除了偏向锁,其他锁机制都用到了CAS;
网友评论