高并发下实现线程安全的i++操作

234 阅读1分钟

高并发下实现线程安全的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++操作。