Java的面向?qū)ο缶幊?/a>">Java的面向?qū)ο缶幊?/a>
748
2022-05-30
文章目錄
總結(jié)
一、指令重排序規(guī)范
二、指令重排序示例
總結(jié)
Java 并發(fā)的 3 3 3 特性 :
原子性 : 每個操作都是
不可拆分的原子操作
; 在線程中進行 a++ 就不是原子操作 , 該操作分為 3 3 3 個步驟 , 首先從主內(nèi)存中讀取 a 變量 , 然后進行自增操作 , 最后在將自增后的值寫回主內(nèi)存中 ;
可見性 :
多個線程
訪問同一個變量 , 該變量一旦被
某個線程修改
, 這些線程必須可以
立刻看到被修改的值 ;
有序性 : 程序按照
代碼先后順序
執(zhí)行 ;
使用 volatile 關(guān)鍵字只能保證 可見性 和 有序性 , 但是不能保證原子性 ;
volatile 可以激活線程共享變量的 "
緩存一致性協(xié)議
" ; 保證
可見性 ;
volatile 可以 禁止 JVM 的 "
指令重排
" ; 保證
有序性 ;
一、指令重排序規(guī)范
指令重排指的是 , 線程中如果兩行代碼
沒有邏輯上的上下關(guān)系
, 可以對代碼進行
重新排序 ;
JVM 指令重排遵循規(guī)范 :
as-if-serial 規(guī)范 : 單個線程中, 指令的重排 ,
不能影響程序的執(zhí)行結(jié)果 ;
可以重排的情況 : 對于下面代碼 ,
兩條指令順序顛倒 , 執(zhí)行結(jié)果相同 ,
可以進行指令重排 ;
x = 0; y = 1;
1
2
不可以進行重排的情況 : 對于下面的代碼 ,
兩條指令如果上下顛倒 , 結(jié)果不同 ,
不可以進行指令重排 ;
x = 0; y = x;
1
2
happens-before 規(guī)范 : 先行發(fā)生原則 ;
二、指令重排序示例
指令重排示例 :
public class Main { // 使用 volatile 關(guān)鍵字修飾變量可以禁止指令重排 /*volatile static int x = 0; volatile static int y = 0; volatile static int a = 0; volatile static int b = 0;*/ // 沒有使用 volatile 關(guān)鍵字修飾, 會產(chǎn)生指令重排的情況 static int x = 0; static int y = 0; static int a = 0; static int b = 0; /** * 多線程運行導(dǎo)致異常值出現(xiàn), 是由于指令重排導(dǎo)致的 * @param args */ public static void main(String[] args) { // 設(shè)置一個非常大的遍歷數(shù) // 指令重排出現(xiàn)過程很少見, 基數(shù)越大, 出現(xiàn)概率越高 for (int i = 0; i < Integer.MAX_VALUE; i ++) { // 每次循環(huán)都初始化變量 x = 0; y = 0; a = 0; b = 0; // 在該線程中, 如果出現(xiàn)指令重排 // 先執(zhí)行 b = 1, 在執(zhí)行 x = a new Thread(new Runnable() { @Override public void run() { x = a; b = 1; } }).start(); // 如果出現(xiàn)指令重排 // 先執(zhí)行 a = 1, 在執(zhí)行 y = b new Thread(new Runnable() { @Override public void run() { y = b; a = 1; } }).start(); try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } /* 執(zhí)行上述代碼, 不是線程 1 先執(zhí)行就是線程 2 先執(zhí)行 如果線程 1 先執(zhí)行, 則 x = 0, y = 1 如果線程 2 先執(zhí)行, 則 x = 1, y = 0 不可能出現(xiàn) x = 1, y = 1 的情況 如果出現(xiàn)了, 則說明線程內(nèi)部的執(zhí)行順序可能被顛倒了 出現(xiàn)了指令重排的情況 */ // 檢查是否有異常值出現(xiàn), 如果出現(xiàn)異常值, 退出方法 if (x == 1 && y == 1) { System.out.println("出現(xiàn)異常值 x = 1, y = 1"); return; } /*else { System.out.println("正常值 x = " + x + ", y = " + y); }*/ } } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
執(zhí)行結(jié)果 :
兩個線程的線程調(diào)度 :
協(xié)同式調(diào)度 : 線程執(zhí)行時間 由 線程 決定 ;
搶占式調(diào)度 : 線程執(zhí)行事件 由 系統(tǒng) 決定 ;
上述示例中的線程調(diào)度方式是 " 搶占式調(diào)度 " , 誰先執(zhí)行
由系統(tǒng)分配
, 這兩個線程的執(zhí)行順序都是隨機的 , 可能線程 1 先執(zhí)行 , 也可能是線程 2 先執(zhí)行 ;
如果線程 1 先執(zhí)行, 則 x = 0, y = 1 ;
如果線程 2 先執(zhí)行, 則 x = 1, y = 0 ;
根據(jù)代碼分析 , 不可能出現(xiàn) x = 1, y = 1 的情況 , 如果出現(xiàn)了, 則說明
線程內(nèi)部的執(zhí)行順序可能被顛倒了
, 出現(xiàn)了指令重排的情況 ;
Java 任務(wù)調(diào)度
版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔相應(yīng)法律責任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實的內(nèi)容,請聯(lián)系我們jiasou666@gmail.com 處理,核實后本網(wǎng)站將在24小時內(nèi)刪除侵權(quán)內(nèi)容。