java并发编程的艺术-学习笔记(2)

java并发机制的底层实现原理

java代码在编译后会变成java字节码,字节码被类加载器加载到JVM中,JVM执行字节码最终需要转换为汇编指令在cpu上运行,并发机制依赖于JVM的实现和cpu指令

  1. volatile
    volatile是轻量级的synchronized,他在多任务处理中保证了共享变量可见性。可见性就是当一个线程修改一个共享变量的时候,另一个线程能够读到这个变量的值。volatile不会引起线程上下文的切换和调度。
    定义: java容许线程访问共享变量,为了确保共享变量能被准确和一致的更新,线程应该确保通过排它锁单独获得这个变量。如果一个变量被声明为volatile,java内存模型确保所有线程看到这个变量的值是一致的

为了提高处理速度,处理器不会直接和内存进行通信,而是把系统内存的数据读取到内部缓存后再进行处理,如果对声明了volatile的变量进行操作,JVM会给处理器发送一条Lock指令,将这个变量的所在缓存行的数据写回到系统内存中在多处理器下,为了保证各个处理器的缓存是一致的,就会实现缓存一致性协议。每个处理器通过嗅探在总线上传播的数据检查自己的缓存是不是过期里,当发现自己缓存对应的内存地址被修改的时,就会将当前处理器的缓存行置为无效,当处理器需要对这个变量进行修改操作的时候,会重新从系统内存把数据读取到处理器缓存中。

  1. synchorized
    对于普通同步方法:锁是当前实例对象
    对于静态同步方法:锁是当前类的class对象
    对于同步方法块:锁的synchorized括号的配置的对象

JVM基于进入和退出Monitor对象来实现方法同步和代码块同步的。
monitorenter指令在编译后插入到同步代码块开始的位置,monitorexit是插入到方法结束处和异常处
JVM要保证每人一个monitorenter要有对应的monitorexit与之配对。任何对象都有一个monitor与之关联,当且还有一个monitor被持有后,它将处于锁定状态。线程执行到monitorenter指令时,将会尝试获取对象所对应的monitor的所有权(获取该对象的锁)

  1. 锁的升级与优化
    锁有四种状态:无锁状态、偏向锁状态、轻量级锁状态、重量级锁状态。这几个状态会随着竞争逐渐升级。锁可以升级但是不能降级,目的:提高获取、释放锁的效率。
    偏向锁:
    java并发编程的艺术-学习笔记(2)
    大多数情况下,锁不仅不存在多线程竞争,而且总是由同一个线程得到,为了减少获得锁的代价引入偏向锁当一个线程访问同步块并获取锁的时,会在对象头和栈帧中的锁记录里存储偏向锁的线程ID,以后该线程进入和退出同步块时不需要进行CAS操作来加锁和释放锁,只需要测试一下对象头里的Mark Word里是否存在指向当前线程的偏向锁。如果成功,表示线程获得了锁。如果失败,则需要在测试一下Mark Word 里的偏向锁标识是不是1,如果没有1,则进行cas竞争锁,如果是1,则尝试使用cas将对象头的偏向锁指向当前线程。

偏向锁的撤销:
**偏向锁使用了一种等到竞争出现才释放锁的机制,所以当其他线程尝试竞争获得偏向锁时,持有偏向锁的线程才会释放锁。**偏向锁的撤销需要到全局安全点才可以。会首先停止拥有偏向锁的线程,然后检查持有偏向锁的线程是否活着。如果这个不活跃,则将对象头设置为无锁状态,如果活跃,拥有偏向锁的栈会被执行,遍历偏向对象的锁记录,栈中的锁记录和对象头的Mark Word要么重新偏向其他线程。要么恢复到无锁状态。最后唤醒暂停的线程。
java并发编程的艺术-学习笔记(2)