Java中对AtomicInteger和int值在多线程下递增操作的测试

时间:2021-07-10 23:09:24

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毫秒的样子.