问题背景
17. 【参考】 volatile 解决多线程内存不可见问题对于一写多读,是可以解决变量同步问题,但是如果多写,同样无法解决线程安全问题。说明: 如果是 count++操作,使用如下类实现:AtomicInteger count = new AtomicInteger();count. addAndGet (1);如果是 JDK8,推荐使用 LongAdder 对象,比 AtomicLong 性能更好(减少乐观锁的重试次数)
“
推荐使用 LongAdder 对象,比 AtomicLong 性能更好(减少乐观锁的重试次数)”
——引自阿里巴巴Java开发手册(黄山版)
真的吗?我们来测试一下
高并发情况下各种累加器性能测试,包括synchronized、AtomicInteger、AtomicLong、LongAdder、LongAccumulator
测试场景
10个线程,每个线程累加一千万次
测试代码
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.LongAccumulator;
import java.util.concurrent.atomic.LongAdder;
class ClickNumber {
int number = 0;
public synchronized void add_synchronized() {
number++;
}
AtomicInteger atomicInteger = new AtomicInteger();
public void add_atomicInteger() {
atomicInteger.incrementAndGet();
}
AtomicLong atomicLong = new AtomicLong();
public void add_atomicLong() {
atomicLong.incrementAndGet();
}
LongAdder longAdder = new LongAdder();
public void add_longAdder() {
longAdder.increment();
}
LongAccumulator accumulator = new LongAccumulator(((left, right) -> left + right), 0);
public void add_accumulator() {
accumulator.accumulate(1);
}
}
public class TestJUC {
public static final int THREAD_NUM = 10;
public static final int THREAD_RUN_TIME = 10000000;
public static void main(String[] args) throws InterruptedException {
ClickNumber clickNumber = new ClickNumber();
long start;
long end;
start = System.currentTimeMillis();
CountDownLatch latch1 = new CountDownLatch(THREAD_NUM);
CountDownLatch latch2 = new CountDownLatch(THREAD_NUM);
CountDownLatch latch3 = new CountDownLatch(THREAD_NUM);
CountDownLatch latch4 = new CountDownLatch(THREAD_NUM);
CountDownLatch latch5 = new CountDownLatch(THREAD_NUM);
for (int i = 0; i < THREAD_NUM; i++) {
new Thread(() -> {
try {
for (int j = 0; j < THREAD_RUN_TIME; j++) {
clickNumber.add_synchronized();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
latch1.countDown();
}
}, String.valueOf(i)).start();
}
latch1.await();
end = System.currentTimeMillis();
System.out.println("\n=========================\n");
System.out.println("add_synchronized耗时 "+(end - start)+" 结果为 "+clickNumber.number);
System.out.println("\n=========================\n");
start = System.currentTimeMillis();
for (int i = 0; i < THREAD_NUM; i++) {
new Thread(() -> {
try {
for (int j = 0; j < THREAD_RUN_TIME; j++) {
clickNumber.add_atomicInteger();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
latch2.countDown();
}
}, String.valueOf(i)).start();
}
latch2.await();
end = System.currentTimeMillis();
System.out.println("add_atomicInteger耗时 "+(end - start)+" 结果为 "+clickNumber.atomicInteger.get());
System.out.println("\n=========================\n");
start = System.currentTimeMillis();
for (int i = 0; i < THREAD_NUM; i++) {
new Thread(() -> {
try {
for (int j = 0; j < THREAD_RUN_TIME; j++) {
clickNumber.add_atomicLong();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
latch3.countDown();
}
}, String.valueOf(i)).start();
}
latch3.await();
end = System.currentTimeMillis();
System.out.println("add_atomicLong耗时 "+(end - start)+" 结果为 "+clickNumber.atomicLong.get());
System.out.println("\n=========================\n");
start = System.currentTimeMillis();
for (int i = 0; i < THREAD_NUM; i++) {
new Thread(() -> {
try {
for (int j = 0; j < THREAD_RUN_TIME; j++) {
clickNumber.add_longAdder();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
latch4.countDown();
}
}, String.valueOf(i)).start();
}
latch4.await();
end = System.currentTimeMillis();
System.out.println("add_longAdder耗时 "+(end - start)+" 结果为 "+clickNumber.longAdder.longValue());
System.out.println("\n=========================\n");
start = System.currentTimeMillis();
for (int i = 0; i < THREAD_NUM; i++) {
new Thread(() -> {
try {
for (int j = 0; j < THREAD_RUN_TIME; j++) {
clickNumber.add_accumulator();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
latch5.countDown();
}
}, String.valueOf(i)).start();
}
latch5.await();
end = System.currentTimeMillis();
System.out.println("add_accumulator耗时 "+(end - start)+" 结果为 "+clickNumber.accumulator.longValue());
}
测试结果
=========================
add_synchronized耗时 6922 结果为 100000000
=========================
add_atomicInteger耗时 1239 结果为 100000000
=========================
add_atomicLong耗时 1150 结果为 100000000
=========================
add_longAdder耗时 288 结果为 100000000
=========================
add_accumulator耗时 305 结果为 100000000
总结
LongAdder真香