高并发下实现线程安全的i++操作
一、使用synchronized
这个比较简单,就是在进行i++操作时,直接使用synchronized加锁,也可以使用Lock加锁,本质都是一样的(锁原理不同),最终都是通过加锁来保证多线程安全的。
public class Synchronized_add {
int i = 0;
public synchronized void add() {
i++;
}
public static void main(String[] args) throws InterruptedException {
long start = System.currentTimeMillis();
Thread[] threads = new Thread[100];
Synchronized_add v = new Synchronized_add();
CountDownLatch latch = new CountDownLatch(threads.length);
for (int i = 0; i < threads.length; i++) {
threads[i] = new Thread(() -> {
try {
// 这里采用CountDownLatch阻塞,保证100个线程都启动成功了之后,多线程同时开始计算
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
for (int n = 0; n < 1000000; n++) {
v.add();
}
});
}
for (int i = 0; i < threads.length; i++) {
threads[i].start();
latch.countDown();
}
for (int i = 0; i < threads.length; i++) {
threads[i].join();
}
System.out.println(v.i);
System.out.println("耗时: " + (System.currentTimeMillis() - start) + "ms");
}
}
耗时: 4568ms
二、使用AtomicInteger
public class AtomicInteger_add {
public static void main(String[] args) throws InterruptedException {
long start = System.currentTimeMillis();
AtomicInteger ai = new AtomicInteger(0);
Thread[] threads = new Thread[100];
for (int i = 0; i < threads.length; i++) {
threads[i] = new Thread(() -> {
for (int n = 0; n < 1000000; n++) {
ai.incrementAndGet();
}
});
}
for (int i = 0; i < threads.length; i++) {
threads[i].start();
}
for (int i = 0; i < threads.length; i++) {
threads[i].join();
}
System.out.println(ai.get());
System.out.println("耗时: " + (System.currentTimeMillis() - start) + "ms");
}
}
耗时: 2657ms
三、使用LongAdder(JDK8及以后才有)
这个类是JDK8版本新增加的,支持原子操作。与AtomicInteger不同的是,它内部采用的分段锁。
public class LongAdder_add {
public static void main(String[] args) throws InterruptedException {
long start = System.currentTimeMillis();
LongAdder longAdder = new LongAdder();
Thread[] threads = new Thread[100];
for (int i = 0; i < threads.length; i++) {
threads[i] = new Thread(() -> {
for (int n = 0; n < 1000000; n++) {
longAdder.add(1);
}
});
}
for (int i = 0; i < threads.length; i++) {
threads[i].start();
}
for (int i = 0; i < threads.length; i++) {
threads[i].join();
}
System.out.println(longAdder.sum());
System.out.println("耗时: " + (System.currentTimeMillis() - start) + "ms");
}
}
耗时: 706ms
四、使用VarHandle(JDK9及以后才有)
这个类是JDK9版本新增加的。VarHandle是文件句柄,内部提供了多个原子操作的方法。
ppublic class VarHandle_add {
int i = 0;
private static VarHandle iHandle;
static {
try {
iHandle = MethodHandles.lookup().findVarHandle(VarHandle_add.class, "i", int.class);
} catch (NoSuchFieldException | IllegalAccessException e) {
e.printStackTrace();
}
}
public static void add(VarHandle_add v) {
iHandle.getAndAdd(v, 1);
}
public static void main(String[] args) throws InterruptedException {
long start = System.currentTimeMillis();
VarHandle_add v = new VarHandle_add();
Thread[] threads = new Thread[100];
for (int i = 0; i < threads.length; i++) {
threads[i] = new Thread(() -> {
for (int n = 0; n < 1000000; n++) {
add(v);
}
});
}
for (int i = 0; i < threads.length; i++) {
threads[i].start();
}
for (int i = 0; i < threads.length; i++) {
threads[i].join();
}
System.out.println(iHandle.get(v));
System.out.println("耗时: " + (System.currentTimeMillis() - start) + "ms");
}
}
耗时: 4762ms
以上耗时统计都是基于同一台电脑,只是一个对比参考,实际数值没有意义。只是在这种场景下,100个线程,每个线程执行100w次i++操作。