要了解 Java中的 volatile 首席要从线程间通讯开始
概念:
- 对于声明了 volatile 的变量进行写的操作时, JVM 会向处理器(CPU) 发送一条 Lock 前缀的指令, 会将这个变量所在缓存行的数据写回到系统内存(
主内存
) - 在多处理器的情况下, 保持各个处理器缓存一致性的特点, 就会实现缓存一致性协议
- 但是,就算写回到内存,如果其他处理器缓存的值还是旧的,再执行计算操作就会有问题。
- 每个处理器通过嗅探在主内存上传播的数据来检查自己缓存的值是不是过期了
- 当处理器发现自己缓存行对应的内存地址被修改,就会将当前处理器的缓存行设置成无效状态,当处理器对这个数据进行修改操作的时候,就会重新从系统内存中把数据读到处理器缓存里。
- 总之 volatile 做的事情是: 在多线程执行中保证共享对象的一致性
synchronized
public class Demo {
private static int i = 0;
public static void main(String[] args) {
synchronized(Demo.class) {
i++;
}
}
}
javap -c -v Demo.class : 结果
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=3, args_size=1
0: ldc #2 // class Demo
2: dup
3: astore_1
4: monitorenter
5: getstatic #3 // Field i:I
8: iconst_1
9: iadd
10: putstatic #3 // Field i:I
13: aload_1
14: monitorexit
15: goto 23
18: astore_2
19: aload_1
20: monitorexit
21: aload_2
22: athrow
23: return
Exception table:
from to target type
5 15 18 any
18 21 18 any
LineNumberTable:
line 6: 0
line 7: 5
line 8: 13
line 9: 23
StackMapTable: number_of_entries = 2
frame_type = 255 /* full_frame */
offset_delta = 18
locals = [ class "[Ljava/lang/String;", class java/lang/Object ]
stack = [ class java/lang/Throwable ]
frame_type = 250 /* chop */
offset_delta = 4
monitorenter : 进入并获取对象监视器
monitorexit : 释放并退出对象监视器
- synchronized获得并释放监视器, 如果两个线程使用了同一个对象锁,监视器能强制保证代码块同时只被一个线程所执行。
- volatile只是在线程内存和“主”内存间同步某个变量的值,而synchronized通过锁定和解锁某个监视器同步所有变量的值。
- 显然synchronized要比volatile消耗更多资源。