Java針對多線程下的數值安全計數器設計了一些類,這些類叫做原子類,其中一部分如下:
1
2
3
4
|
java.util.concurrent.atomic.AtomicBoolean; java.util.concurrent.atomic.AtomicInteger; java.util.concurrent.atomic.AtomicLong; java.util.concurrent.atomic.AtomicReference; |
下面是一個對比 AtomicInteger 與 普通 int 值在多線程下的遞增測試,使用的是 junit4;
完整代碼:
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
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
|
package test.java; import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicInteger; import org.junit.Assert; import org.junit.Before; import org.junit.Test; /** * 測試AtomicInteger與普通int值在多線程下的遞增操作 */ public class TestAtomic { // 原子Integer遞增對象 public static AtomicInteger counter_integer; // = new AtomicInteger(0); // 一個int類型的變量 public static int count_int = 0 ; @Before public void setUp() { // 所有測試開始之前執行初始設置工作 counter_integer = new AtomicInteger( 0 ); } @Test public void testAtomic() throws InterruptedException { // 創建的線程數量 int threadCount = 100 ; // 其他附屬線程內部循環多少次 int loopCount = 10000600 ; // 控制附屬線程的輔助對象;(其他await的線程先等著主線程喊開始) CountDownLatch latch_1 = new CountDownLatch( 1 ); // 控制主線程的輔助對象;(主線程等著所有附屬線程都運行完畢再繼續) CountDownLatch latch_n = new CountDownLatch(threadCount); // 創建并啟動其他附屬線程 for ( int i = 0 ; i < threadCount; i++) { Thread thread = new AtomicIntegerThread(latch_1, latch_n, loopCount); thread.start(); } long startNano = System.nanoTime(); // 讓其他等待的線程統一開始 latch_1.countDown(); // 等待其他線程執行完 latch_n.await(); // long endNano = System.nanoTime(); int sum = counter_integer.get(); // Assert.assertEquals( "sum 不等于 threadCount * loopCount,測試失敗" , sum, threadCount * loopCount); System.out.println( "--------testAtomic(); 預期兩者相等------------" ); System.out.println( "耗時: " + ((endNano - startNano) / ( 1000 * 1000 )) + "ms" ); System.out.println( "threadCount = " + (threadCount) + ";" ); System.out.println( "loopCount = " + (loopCount) + ";" ); System.out.println( "sum = " + (sum) + ";" ); } @Test public void testIntAdd() throws InterruptedException { // 創建的線程數量 int threadCount = 100 ; // 其他附屬線程內部循環多少次 int loopCount = 10000600 ; // 控制附屬線程的輔助對象;(其他await的線程先等著主線程喊開始) CountDownLatch latch_1 = new CountDownLatch( 1 ); // 控制主線程的輔助對象;(主線程等著所有附屬線程都運行完畢再繼續) CountDownLatch latch_n = new CountDownLatch(threadCount); // 創建并啟動其他附屬線程 for ( int i = 0 ; i < threadCount; i++) { Thread thread = new IntegerThread(latch_1, latch_n, loopCount); thread.start(); } long startNano = System.nanoTime(); // 讓其他等待的線程統一開始 latch_1.countDown(); // 等待其他線程執行完 latch_n.await(); // long endNano = System.nanoTime(); int sum = count_int; // Assert.assertNotEquals( "sum 等于 threadCount * loopCount,testIntAdd()測試失敗" , sum, threadCount * loopCount); System.out.println( "-------testIntAdd(); 預期兩者不相等---------" ); System.out.println( "耗時: " + ((endNano - startNano) / ( 1000 * 1000 ))+ "ms" ); System.out.println( "threadCount = " + (threadCount) + ";" ); System.out.println( "loopCount = " + (loopCount) + ";" ); System.out.println( "sum = " + (sum) + ";" ); } // 線程 class AtomicIntegerThread extends Thread { private CountDownLatch latch = null ; private CountDownLatch latchdown = null ; private int loopCount; public AtomicIntegerThread(CountDownLatch latch, CountDownLatch latchdown, int loopCount) { this .latch = latch; this .latchdown = latchdown; this .loopCount = loopCount; } @Override public void run() { // 等待信號同步 try { this .latch.await(); } catch (InterruptedException e) { e.printStackTrace(); } // for ( int i = 0 ; i < loopCount; i++) { counter_integer.getAndIncrement(); } // 通知遞減1次 latchdown.countDown(); } } // 線程 class IntegerThread extends Thread { private CountDownLatch latch = null ; private CountDownLatch latchdown = null ; private int loopCount; public IntegerThread(CountDownLatch latch, CountDownLatch latchdown, int loopCount) { this .latch = latch; this .latchdown = latchdown; this .loopCount = loopCount; } @Override public void run() { // 等待信號同步 try { this .latch.await(); } catch (InterruptedException e) { e.printStackTrace(); } // for ( int i = 0 ; i < loopCount; i++) { count_int++; } // 通知遞減1次 latchdown.countDown(); } } } |
普通PC機上的執行結果類似如下:
1
2
3
4
5
6
7
8
9
10
|
--------------testAtomic(); 預期兩者相等------------------- 耗時: 85366ms threadCount = 100; loopCount = 10000600; sum = 1000060000; --------------testIntAdd(); 預期兩者不相等------------------- 耗時: 1406ms threadCount = 100; loopCount = 10000600; sum = 119428988; |
從中可以看出, AtomicInteger操作 與 int操作的效率大致相差在50-80倍上下,當然,int很不消耗時間,這個對比只是提供一個參照。
如果確定是單線程執行,那應該使用 int; 而int在多線程下的操作執行的效率還是蠻高的, 10億次只花了1.5秒鐘;
(假設CPU是 2GHZ,雙核4線程,理論最大8GHZ,則每秒理論上有80億個時鐘周期,
10億次Java的int增加消耗了1.5秒,即 120億次運算, 算下來每次循環消耗CPU周期 12個;
個人覺得效率不錯, C 語言也應該需要4個以上的時鐘周期(判斷,執行內部代碼,自增判斷,跳轉)
前提是: JVM和CPU沒有進行激進優化.
)
而 AtomicInteger 效率其實也不低,10億次消耗了80秒, 那100萬次大約也就是千分之一,80毫秒的樣子.