【问题标题】:When is AtomicInteger preferrable over synchronized?什么时候 AtomicInteger 比同步更可取?
【发布时间】:2012-07-25 02:55:12
【问题描述】:

既然AtomicInteger 至少比受synchronized 保护的int 慢一个数量级,我为什么要使用AtomicInteger?

例如,如果我只想以线程安全的方式增加int 的值,为什么不使用always

synchronized(threadsafeint) {
  threadsafeint++;
}

而不是使用慢得多的AtomicInteger.incrementAndGet()?

【问题讨论】:

    标签: java multithreading performance concurrency atomic


    【解决方案1】:

    由于 AtomicInteger 至少比受同步保护的 int 慢一个数量级,我为什么要使用 AtomicInteger?

    AtomicInteger 更快。

    static final Object LOCK1 = new Object();
    static final Object LOCK2 = new Object();
    static int i1 = 0;
    static int i2 = 0;
    static final AtomicInteger ai1 = new AtomicInteger();
    static final AtomicInteger ai2 = new AtomicInteger();
    
    public static void main(String... args) throws IOException {
        for(int i=0;i<5;i++) {
            testSyncInt();
            testAtomicInt();
        }
    }
    
    private static void testSyncInt() {
        long start = System.nanoTime();
        int runs = 10000000;
        for(int i=0;i< runs;i+=2) {
            synchronized (LOCK1) {
                i1++;
            }
            synchronized (LOCK2) {
                i2++;
            }
        }
        long time = System.nanoTime() - start;
        System.out.printf("sync + incr: Each increment took an average of %.1f ns%n", (double) time/runs);
    }
    
    private static void testAtomicInt() {
        long start = System.nanoTime();
        int runs = 10000000;
        for(int i=0;i< runs;i+=2) {
            ai1.incrementAndGet();
            ai2.incrementAndGet();
        }
        long time = System.nanoTime() - start;
        System.out.printf("incrementAndGet: Each increment took an average of %.1f ns%n", (double) time/runs);
    }
    

    打印

    sync + incr: Each increment took an average of 32.4 ns
    incrementAndGet: Each increment took an average of 20.6 ns
    sync + incr: Each increment took an average of 31.4 ns
    incrementAndGet: Each increment took an average of 12.9 ns
    sync + incr: Each increment took an average of 29.6 ns
    incrementAndGet: Each increment took an average of 12.9 ns
    sync + incr: Each increment took an average of 35.1 ns
    incrementAndGet: Each increment took an average of 16.6 ns
    sync + incr: Each increment took an average of 29.9 ns
    incrementAndGet: Each increment took an average of 13.0 ns
    

    按照@assylias 的建议添加一些争用。它表明,当你真正只使用一个线程时,CPU 可以优化访问。

    static final Object LOCK1 = new Object();
    static final Object LOCK2 = new Object();
    static int i1 = 0;
    static int i2 = 0;
    static final AtomicInteger ai1 = new AtomicInteger();
    static final AtomicInteger ai2 = new AtomicInteger();
    
    public static void main(String... args) throws  ExecutionException, InterruptedException {
        for(int i=0;i<5;i++) {
            testSyncInt();
            testAtomicInt();
        }
    }
    
    private static void testSyncInt() throws ExecutionException, InterruptedException {
        long start = System.nanoTime();
        final int runs = 1000000;
        ExecutorService es = Executors.newFixedThreadPool(2);
        List<Future<Void>> futures = new ArrayList<>();
        for(int t=0;t<8;t++) {
            futures.add(es.submit(new Callable<Void>() {
                public Void call() throws Exception {
                    for (int i = 0; i < runs; i += 2) {
                        synchronized (LOCK1) {
                            i1++;
                        }
                        synchronized (LOCK2) {
                            i2++;
                        }
                    }
                    return null;
                }
            }));
        }
        for (Future<Void> future : futures) {
            future.get();
        }
        es.shutdown();
        long time = System.nanoTime() - start;
        System.out.printf("sync + incr: Each increment took an average of %.1f ns%n", (double) time/runs/2);
    }
    
    private static void testAtomicInt() throws ExecutionException, InterruptedException {
        long start = System.nanoTime();
        final int runs = 1000000;
        ExecutorService es = Executors.newFixedThreadPool(2);
        List<Future<Void>> futures = new ArrayList<>();
        for(int t=0;t<8;t++) {
            futures.add(es.submit(new Callable<Void>() {
                public Void call() throws Exception {
                    for (int i = 0; i < runs; i += 2) {
                        ai1.incrementAndGet();
                        ai2.incrementAndGet();
                    }
                    return null;
                }
            }));
        }
        for (Future<Void> future : futures) {
            future.get();
        }
        es.shutdown();
        long time = System.nanoTime() - start;
        System.out.printf("incrementAndGet: Each increment took an average of %.1f ns%n", (double) time/runs/2);
    }
    

    打印

    sync + incr: Each increment took an average of 478.6 ns
    incrementAndGet: Each increment took an average of 191.5 ns
    sync + incr: Each increment took an average of 437.5 ns
    incrementAndGet: Each increment took an average of 169.8 ns
    sync + incr: Each increment took an average of 408.1 ns
    incrementAndGet: Each increment took an average of 180.8 ns
    sync + incr: Each increment took an average of 511.5 ns
    incrementAndGet: Each increment took an average of 313.4 ns
    sync + incr: Each increment took an average of 441.6 ns
    incrementAndGet: Each increment took an average of 219.7 ns
    

    【讨论】:

    • 我现在真的很困惑。这不是我从@Gray 的回答中所理解的:stackoverflow.com/a/11125474/722603我错过了什么?
    • 我认为他的意思是 AtomicIntegerunsynchronized int 慢;他在谈论为什么你不应该在没有正当理由的情况下用AtomicInteger 替换所有int 成员。但这不是慢一个数量级,而是快一个数量级。在大多数情况下,这些都是我们正在谈论的微小差异。
    • 我记得在某处读到,Atomic 的锁定机制也不同,但现在无法获得该参考。我也可能错了。
    • @thinksteep - 原子类型通常不使用锁定。他们使用原子“比较和交换”指令等。参考:ibm.com/developerworks/java/library/j-jtp11234
    • @StephenC:感谢您提供的链接,它将刷新我的理解。
    【解决方案2】:

    如果您真的想详细了解为什么 java.util.concurrent 的东西更好,以及与经典同步方法相比有何不同,请阅读 this link(以及整个博客)

    【讨论】:

      猜你喜欢
      • 2021-03-18
      • 1970-01-01
      • 2015-12-02
      • 1970-01-01
      • 2019-08-27
      • 2015-04-05
      • 2010-11-20
      • 1970-01-01
      相关资源
      最近更新 更多