Java 多線程與高并發
保證線程可見性
當多個線程訪問同一個共享資源時,線程會拷貝資源的副本到自己的工作內存。這樣如果某個線程對這個資源進行寫操作,其他線程不會馬上知道。當對這個資源加volatile關鍵字,其他線程就會隨時監聽,更新新的值。
如下例子,不加volatile關鍵字,線程不會停止,加volatile關鍵字后會及時重新更新副本stop的值,線程停止。
package com.nobody.thread; /** * 不加volatile,輸出: * main start... * thread start... * change stop=true * * 加volatile,輸出: * main start... * thread start... * thread stop... * change stop=true * @author Μr.ηobοdy * * @date 2020-04-19 * */ public class VolatileDemo { private /* volatile */ static boolean stop = false; public static void main(String[] args) { Thread t = new Thread(() -> { System.out.println("thread start..."); while (!stop) { } System.out.println("thread stop..."); }); System.out.println("main start..."); t.start(); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } stop = true; System.out.println("change stop=" + stop); } }
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
禁止指令重排序
JIT(即時編譯器just-in-time compiler) 是一種提高程序運行效率的方法,會將指令重排序。例如實例化一個對象,一般可分為3步驟,第一分配內存空間,第二初始化變量等,第三將引用地址賦值給引用對象。指令重排序可將順序改為132。這樣引用對象可能就拿到一個未初始化的對象,導致出錯。
package com.nobody.thread; /** * 單例模式(懶漢式) * 懶漢式必須加volatile * * @author Μr.ηobοdy * * @date 2020-04-19 * */ public class Singleton { private /* vovalite */ static Singleton INSTANCE; private String name; private Singleton(String name) { this.name = name; } public static Singleton getInstance() { if (null == INSTANCE) { synchronized (Singleton.class) { if (null == INSTANCE) { // 可能會出現指令重排序,即未進行成員變量name的初始化就退出了, // 這樣別人就會拿到未初始化(name=null)的Singleton對象 INSTANCE = new Singleton("hh"); } } } return INSTANCE; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
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
不保證原子性
package com.nobody.thread; import java.util.ArrayList; import java.util.List; /** * volatile不保證原子性,最終結果一般小于10000 * * 若要保證原子性,直接將doCount方法加synchronized關鍵字即可,而volatile可有可無 * * @author Μr.ηobοdy * * @date 2020-04-19 * */ public class VolatileDemo1 { private volatile static int count = 0; private /*synchronized*/ void doCount() { for (int i = 0; i < 1000; i++) { count++; } } public static void main(String[] args) { VolatileDemo1 v = new VolatileDemo1(); // 啟動10個線程 List
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
設置新值之前會先將舊的值與期望值比較,如果相等才set,不然就重試或者失敗。這是有CPU原語支持的。
package com.nobody.thread; import java.util.ArrayList; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; /** * CAS AtomicInteger保證原子性,最終結果一定等于10000 * * * @author Μr.ηobοdy * * @date 2020-04-19 * */ public class AtomicIntegerDemo { private static AtomicInteger count = new AtomicInteger(0); private void doCount() { for (int i = 0; i < 1000; i++) { count.incrementAndGet(); } } public static void main(String[] args) { AtomicIntegerDemo v = new AtomicIntegerDemo(); // 啟動10個線程 List
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
不過這種會出現ABA問題,即由值A先變成值B,然后又變回A值,最后舊值與期望值比較還是相等。可用版本號解決這個問題。
采用分段鎖思想,假如有1000個線程對同一個共享變量進行操作(例如自增),此處假設分為4小組,250個線程為1組,組內進行自增操作,這樣分組能減少鎖的概率,最后將每個小組進行求總和處理。其實分段鎖組內還是CAS原理。一般在線程數高時,效率比synchronized和AtomicLong高。
package com.nobody.thread; import java.util.ArrayList; import java.util.List; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.LongAdder; /** * LongAdder,AtomicLong,synchronized多線程時效率比較 * 模擬1000個線程對一個等于0的值進行自增操作,每個線程自增10000 * * 輸出結果: * longAdderCount:10000000, time:227 * atomicLongCount:10000000, time:395 * synchronizedCount:10000000, time:909 * * @author Μr.ηobοdy * * @date 2020-04-20 * */ public class LongAdderDemo { private static LongAdder longAdderCount = new LongAdder(); private static AtomicLong atomicLongCount = new AtomicLong(0L); private static long synchronizedCount = 0L; public static void main(String[] args) { // LongAdder測試 List
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
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
Java 任務調度 多線程
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。