volatile
volatile的作用
当一个变量被volatile关键字修饰之后,就确保了:
- 可见性。当一个线程对这个值修改之后,其他线程会得到其最新值
- 有序性。禁止指令重排
volatile关键字并不能保证变量的原子性。
volatile关键字的原理:
在加入volatile关键字之后,会多出一个lock前缀指令,相当于一个内存屏障,有三个功能:
- 确保指令重排的时候,不会把屏障前面的指令排到屏障后面,也不会把屏障后面的指令拍到屏障前面
- 强制对缓存的修改写入主存
- 如果是写操作,会使其他cpu的缓存无效
volatile关键字的使用场景:
- 标记状态量
volatile boolean flag = ture;
while (flag) {
doSometing();
}
- 双重校验
public class Singleton() {
private volatile static Singleton instance = null;
private Singleton(){};
public static Singleton getInstance() {
if (instance == null) {
synchronized(Singleton.class){
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
synchronized
synchronized的用法:
| 分类 | 具体分类 | 被锁对象 | 伪代码 |
|---|---|---|---|
| 方法 | 实例方法 | 类的实例对象 | public synchronized void method(){} |
| 方法 | 静态方法 | 类对象 | public synchronized static void method(){} |
| 代码块 | 实例对象 | 类的实例对象 | synchronized(this) {} |
| 代码块 | class对象 | 类对象 | synchronized(SynchronizedTest.class){} |
| 代码块 | 任意实例对象object | 实例对象object | String lock = "lock"; synhronized(lock){} |
如果锁的是类对象的话,尽管new了多个对象,但他们之间任然属于同一个类,会被锁住。即线程之间保证同步关系。
sychronized原理
- 同步代码块 在执行synchronized代码块的时候,使用monitorenter指令,在执行完之后,使用monitorexit指令。每个对象有一个计数器,当获取到monitor时,计数器加一,当释放monitor时,计数器减一。在执行monitorenter指令时,当前线程先试图获取对象锁的monitor,当计数器为0的时候,获取成功,并将计数器置为1。如果当前线程拥有objectref的monitor持有权,再次进入的时候,无需执行monitorenter指令,并讲计数器加1,在执行monitorexit时,将计数器置为0。其它线程有机会获取monitor的持有权。
public void test() {
synchronized(this) {
}
}
字节码
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=2, locals=3, args_size=1
0: aload_0
1: dup
2: astore_1
3: monitorenter
4: aload_1
5: monitorexit
6: goto 14
9: astore_2
10: aload_1
11: monitorexit
12: aload_2
13: athrow
14: return
...
- 同步方法 同步方法是隐式的,通过ACC_SYNCHRONIZED标识来区分一个方法是否是同步方法。在调用方法时,如果检测到有ACC_SYNCHRONIZED标识,执行线程将先持有monitor,在方法执行结束后(无论是正常执行结束还是抛出异常),释放monitor。在该线程持有monitor时,其他线程无法获得同一个monitor。
public synchronized void test() {}
public synchronized void test();
descriptor: ()V
flags: ACC_PUBLIC, ACC_SYNCHRONIZED
Code:
stack=0, locals=1, args_size=1
0: return
LineNumberTable:
line 14: 0
LocalVariableTable:
Start Length Slot Name Signature
0 1 0 this LSynchronizedTest;
...