问题描述
在大并发下使用 AtomicInteger ,相比于使用锁,Atomic会不断自旋重试,在线程比较多的场景下会造成cpu资源消耗高么?性能谁更高?
解释
在大并发场景下,使用 AtomicInteger
相比使用显式的锁(如 synchronized
)确实有一些区别和考虑因素。
-
自旋和CPU消耗:
AtomicInteger
使用了一些技术(如 CAS,即比较并交换),它会尝试更新值直到成功。如果更新失败,它会自旋(重试)。在低并发情况下,自旋几乎没有额外开销,因为更新通常很快成功。- 在高并发情况下,自旋可能会导致一定的CPU资源消耗。如果有大量线程竞争同一个
AtomicInteger
,并且更新操作不断失败,导致频繁自旋,会增加CPU负载。
-
性能比较:
AtomicInteger
在低到中等并发情况下通常比显式锁(如synchronized
)效率更高,因为它利用了硬件级别的原子操作,避免了线程阻塞和切换的开销。- 在非常高的并发情况下,性能可能会受到影响,因为自旋操作可能会消耗大量CPU资源,而且不能保证性能和可扩展性的优势。
-
选择合适的工具:
- 在选择
AtomicInteger
还是显式锁时,需要考虑具体的应用场景和并发需求。通常情况下,AtomicInteger
更适合用于简单的计数或者状态标记等,而显式锁更适合于需要复杂的条件同步和数据操作的场景。
- 在选择
综上所述,虽然 AtomicInteger
在大多数情况下性能优于显式锁,但在极端高并发情况下,它可能会因为自旋而增加CPU消耗。因此,在高并发场景下,需要进行性能测试和基准测试,以便选择最适合的并发控制方法。
代码演示
1、使用atomic
/**
* 使用-AtomicLong,进行 100个线程并发进行 i++ 100万次,测试总耗时
*/
public static void testAtomicLongAdd() {
AtomicLong atomicLong = new AtomicLong(0);
CountDownLatch latch = new CountDownLatch(100 * 100_0000);
StopWatch stopWatch = new StopWatch();
stopWatch.start();
for (int i = 0; i < 100; i++) {
new Thread(() -> {
for (int j = 0; j < 100_0000; j++) {
try {
atomicLong.incrementAndGet();
} finally {
latch.countDown();
}
}
}).start();
}
try {
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
stopWatch.stop();
System.out.println("AtomicTest.testAtomicLongAdd() total count: " + atomicLong.get());
System.out.println("AtomicTest.testAtomicLongAdd() total time: " + stopWatch.getTotalTimeMillis() + " ms");
System.out.println("============================================================================");
}
2、使用reentrantLock
/**
* 使用锁-ReentrantLock,进行 100个线程并发进行 i++ 100万次,测试总耗时
*/
public static void testReentrantLockAdd() {
Lock reentrantLock = new ReentrantLock();
CountDownLatch latch = new CountDownLatch(100 * 100_0000);
StopWatch stopWatch = new StopWatch();
stopWatch.start();
for (int i = 0; i < 100; i++) {
new Thread(() -> {
for (int j = 0; j < 100_0000; j++) {
reentrantLock.lock();
try {
count++;
} finally {
reentrantLock.unlock();
latch.countDown();
}
}
}).start();
}
try {
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
stopWatch.stop();
System.out.println("AtomicTest.testReentrantLockAdd() total count: " + count);
System.out.println("AtomicTest.testReentrantLockAdd() total time: " + stopWatch.getTotalTimeMillis() + " ms");
}
3、演示结果
public static void main(String[] args) {
testAtomicLongAdd();
testReentrantLockAdd();
}
改成两个线程并发
结果:Atomic效率则高于加锁