volatile是轻量级的synchronized。如果volatile变量修饰符使用恰当的话,它比synchronized的使用和执行成本更低,因为它不会引起线程上下文的切换和调度。
volatile:对任意单个volatile变量的读/写具有原子性,但类似于volatile++这种复合操作不
具有原子性
同时,volatile变量的读/写和CAS可以实现线程之间的通信。一个通用的模式:
1.首先,声明共享变量为volatile
2.然后,使用CAS的原子条件更新来实现线程之间的同步
3.同时,配合以volatile的读/写和CAS所具有的volatile读和写的内存语义来实现线程之间的通信
双重检查锁定使用Volatile的原因是为了保证在多线程情况下禁止重排序(初始化对象和设置Instance指向内存空间)。
容易忘记的点:在退出和抛出异常的时候都会释放锁。
代码块同步是使用monitorenter和monitorexit指令实现的
内存语义:
进入 synchronized 块的内存语义:是把在 synchronized 块内使用到的变量从线程的工作内存
中清除,这样在 synchronized 块内使用到该变量时就不会从线程的工作内存中获取,而是
直接从主内存中获取 。 退出 synchronized 块的内存语义是把在 synchronized 块内对共享变
量的修改刷新到主内存 。
volatile 的内存语义和 synchronized 有相似之处:当线程写入了 volatile 变量值时就等价于线程退出 synchronized同步块(把写入工作内存的变量值同步到主内存),读取 volati le 变量值时就相当于进入同步块 ( 先清空本地内存变量值,再从主 内存获取最新值) 。
下面是volatile进行读写操作的内存语义图:
如果对声明了volatile的变量进行写操作,JVM就会向处理器发送一条Lock前缀的指令,将这个变量所在缓存行的数据写回到系统内存。但是,就算写回到内存,如果其他处理器缓存的值还是旧的,再执行计算操作就会有问题。所以,在多处理器下,为了保证各个处理器的缓存是一致的,就会实现缓存一致性协议,每个处理器通过嗅探在总线上传播的数据来检查自己缓存的值是不是过期了,当处理器发现自己缓存行对应的内存地址被修改,就会将当前处理器的缓存行设置成无效状态,当处理器对这个数据进行修改操作的时候,会重新从系统内存中把数据读到处理器缓存里.
------<选取Java并发编程的艺术2.1节>
synchronized用的锁是存在Java对象头里的。如果对象是数组类型,则虚拟机用3个字宽
(Word)存储对象头,如果对象是非数组类型,则用2字宽存储对象头
保证原子性:1.使用总线锁定保证原子性 2.使用缓存锁来保证原子性。