保證了這個變量對所有線程的可見性
禁止指令重排序優化
關鍵字volatile可以保證變量對所有線程的可見性,也就是當一個線程修改了這個變量的值,其他線程能夠立即得到修改的值。普通變量是做不到這樣,普通變量的值需要通過主內存在線程之間傳遞。舉個例子:線程A修改一個普通變量的值,然後傳送給主內存,另外一個線程B需要等到傳送完主內存後才能夠從主內存進行讀取操作,這樣變量最新的值才會對線程B可見。先看下如下例子,代碼如下所示:/**
* Created by TanJiaJun on 2020-08-16.
*/
class VolatileDemo {
private static final int THREADS_COUNT = 10;
private static volatile int value = 0;
private static void increase() {
// 對value變量進行自增操作
value++;
}
public static void main(String[] args) {
// 創建10個線程
Thread[] threads = new Thread[THREADS_COUNT];
for (int i = 0; i < THREADS_COUNT; i++) {
threads[i] = new Thread(() -> {
for (int j = 0; j < 1000; j++)
// 每個線程對value變量進行1000次自增操作
increase();
});
threads[i].start();
}
// 主線程等待子線程運行結束
for (Thread thread : threads) {
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("value的值:" + value);
}
}
private static void increase();
descriptor: ()V
flags: (0x000a) ACC_PRIVATE, ACC_STATIC
Code:
stack=2, locals=0, args_size=0
0: getstatic #7 // Field value:I
3: iconst_1
4: iadd
5: putstatic #7 // Field value:I
8: return
LineNumberTable:
line 12: 0
line 13: 8
int i = 1;
int j = 2;
int k = i + j;
將常量1賦值給i
將常量2賦值給j
取到i的值
取到j的值
將i的值和j的值相加後賦值給k
在上面這五個步驟中,步驟1可能會和步驟2和步驟4重排序,步驟2可能會和步驟1和步驟3重排序,步驟3可能會和步驟2和步驟4重排序,步驟4可能會和步驟1和步驟3重排序,但是步驟1、步驟3和步驟5之間不能重排序,步驟2、步驟4和步驟5之間不能重排序,因為它們之間存在依賴關係,一旦重排序,線程表現為串行的語義將無法得到保證。再看個例子,使用雙重檢查鎖定(DCL)實現單例模式,代碼如下所示:/**
* Created by TanJiaJun on 2020/8/23.
*/
class Singleton {
// 用關鍵字volatile修飾變量sInstance,禁止指令重排序優化
private static volatile Singleton sInstance;
// 私有構造方法
private Singleton() {
// 防止通過反射調用構造方法導致單例失效
if (sInstance != null)
throw new RuntimeException("Cannot construct a singleton more than once.");
}
// 獲取單例的方法
public static Singleton getInstance() {
// 第一次判斷sInstance是否為空,用於判斷是否需要同步,提高性能和效率
if (sInstance == null) {
// 使用synchronized修飾代碼塊,取Singleton的Class對象作為鎖對象
synchronized (Singleton.class) {
// 第二次判斷sInstance是否為空,用於判斷是否已經創建實例
if (sInstance == null) {
// 創建Singleton對象
sInstance = new Singleton();
}
}
}
// 返回sInstance
return sInstance;
}
public static void main(String[] args) {
Singleton.getInstance();
}
}
0x000000011b33f4c7: mov 0x38(%rsp),%rax
0x000000011b33f4cc: movabs $0x61ff0ac48,%rdx ; {oop(a 'java/lang/Class'{0x000000061ff0ac48} = 'Singleton')}
0x000000011b33f4d6: movsbl 0x30(%r15),%esi
0x000000011b33f4db: cmp $0x0,%esi
0x000000011b33f4de: jne 0x000000011b33f6e9
0x000000011b33f4e4: mov %rax,%r10
0x000000011b33f4e7: shr $0x3,%r10
0x000000011b33f4eb: mov %r10d,0x70(%rdx)
0x000000011b33f4ef: lock addl $0x0,-0x40(%rsp)
0x000000011b33f4f5: mov %rdx,%rsi
0x000000011b33f4f8: xor %rax,%rsi
0x000000011b33f4fb: shr $0x15,%rsi
0x000000011b33f4ff: cmp $0x0,%rsi
0x000000011b33f503: jne 0x000000011b33f708 ;*putstatic sInstance {reexecute=0 rethrow=0 return_oop=0}
; - Singleton::getInstance@24 (line 25)
0x0000000116f2a4c7: mov 0x38(%rsp),%rax
0x0000000116f2a4cc: movabs $0x61ff0acb8,%rdx ; {oop(a 'java/lang/Class'{0x000000061ff0acb8} = 'Singleton')}
0x0000000116f2a4d6: movsbl 0x30(%r15),%esi
0x0000000116f2a4db: cmp $0x0,%esi
0x0000000116f2a4de: jne 0x0000000116f2a6e1
0x0000000116f2a4e4: mov %rax,%r10
0x0000000116f2a4e7: shr $0x3,%r10
0x0000000116f2a4eb: mov %r10d,0x70(%rdx)
0x0000000116f2a4ef: mov %rdx,%rsi
0x0000000116f2a4f2: xor %rax,%rsi
0x0000000116f2a4f5: shr $0x15,%rsi
0x0000000116f2a4f9: cmp $0x0,%rsi
0x0000000116f2a4fd: jne 0x0000000116f2a700 ;*putstatic sInstance {reexecute=0 rethrow=0 return_oop=0}
; - Singleton::getInstance@24 (line 25)
/**
* Created by TanJiaJun on 2020/8/23.
*/
class MemoryBarrierTest {
private int a, b;
private volatile int c, d;
private void test() {
int i, j;
i = a; // load a
j = b; // load b
i = c; // load c
// LoadLoad
j = d; // load d
// LoadStore
a = i; // store a
b = j; // store b
// StoreStore
c = i; // store c
// StoreStore
d = j; // store d
// StoreLoad
i = d; // load d
// LoadLoad
// LoadStore
j = b; // load b
a = i; // store a
}
}