【问题标题】:AtomicInteger is slower than synchronizedAtomicInteger 比同步慢
【发布时间】:2015-12-02 01:14:17
【问题描述】:

我通过比较同步方法测试了多线程中的原子整数有多快,但我得到的结果是原子整数比同步方法慢。

我阅读了 Java API 参考,并且了解 Atomic Integer 比多线程中的同步更快。

所以我想知道为什么我用 Atomic Integer 得到了不好的结果。 我的测试代码错了?

这是我的代码

import java.util.concurrent.atomic.AtomicInteger;

public class AtomicThreadTest {

    // Number of thread to run
    private static final int THREAD_NUM = 1000;
    // Repetition number to count up
    private static final int ROOP_NUM = 200000;

    // Base counter class
    private abstract class Counter implements Runnable {
        @Override
        public void run() {
            for (int i = 0; i < ROOP_NUM; i++) {
                increment();
            }
        }
        // Increment method
        public abstract void increment();
        // Get result of calculation
        public abstract int getResult();
        // Get name of the class
        @Override
        public String toString() {
            return getClass().getSimpleName();
        }
    }

    // Use no thread safe
    private static int count = 0;
    private class NoThreadSafeCounter extends Counter {
        @Override
        public void increment() {
            count++;
        }
        @Override
        public int getResult() {
            return count;
        }
    }

    // Use volatile
    private static volatile int volatileCount = 0;
    private class VolatileCounter extends Counter {
        @Override
        public void increment() {
            volatileCount++;
        }
        @Override
        public int getResult() {
            return volatileCount;
        }
    }

    // Use synchronized
    private static int synchronizedCount = 0;
    private class SynchronizedCounter extends Counter {
        @Override
        public synchronized void increment() {
            synchronizedCount++;
        }
        @Override
        public int getResult() {
            return synchronizedCount;
        }
    }

    // Use AtomicInteger
    private static AtomicInteger atomicCount = new AtomicInteger(0);
    private class AtomicCounter extends Counter {
        @Override
        public void increment() {
            atomicCount.incrementAndGet();
        }
        @Override
        public int getResult() {
            return atomicCount.get();
        }
    }

    public static void main(String[] args) {
        AtomicThreadTest testClass = new AtomicThreadTest();
        Counter[] testCounter = {
                testClass.new NoThreadSafeCounter(),
                testClass.new VolatileCounter(),
                testClass.new SynchronizedCounter(),
                testClass.new AtomicCounter(),
        };

        for (Counter c : testCounter) {
            System.out.println("-------------------------");
            System.out.printf("Test for class : %s\n", c.toString());
            try {
                test(c);
            } catch (InterruptedException e) {
                System.out.println("Test halted");
            } finally {
                System.out.println("");
                System.gc();
            }
        }
        System.out.println("-------------------------");
    }

    public static void test(final Counter counter) throws InterruptedException {
        System.out.printf("Start with threads : %d, roop : %d\n", THREAD_NUM, ROOP_NUM);
        final long startTime = System.currentTimeMillis();

        // Create THREAD_NUM threads and run them
        Thread[] threads = new Thread[THREAD_NUM];

        for (int i = 0; i < THREAD_NUM; i++) {
            threads[i] = new Thread(counter);
            threads[i].start();
        }

        // Wait for all threads other than this end
        while (Thread.activeCount() > 1) {
            Thread.sleep(10);
        }

        final long endTime = System.currentTimeMillis();
        System.out.printf("Result %d, Expected %d\n", counter.getResult(), THREAD_NUM*ROOP_NUM);
        System.out.printf("Time to calc : %d\n", endTime-startTime);
    }
}

我得到了下面的结果。

-------------------------
Test for class : NoThreadSafeCounter
Start with threads : 1000, roop : 200000
Result 198785583, Expected 200000000
Time to calc : 127

-------------------------
Test for class : VolatileCounter
Start with threads : 1000, roop : 200000
Result 19162116, Expected 200000000
Time to calc : 4458

-------------------------
Test for class : SynchronizedCounter
Start with threads : 1000, roop : 200000
Result 200000000, Expected 200000000
Time to calc : 8426

-------------------------
Test for class : AtomicCounter
Start with threads : 1000, roop : 200000
Result 200000000, Expected 200000000
Time to calc : 15190

【问题讨论】:

  • 你不应该调用System.gc(),因为你不知道它什么时候会被执行。您没有使用大量内存,在执行期间您不太可能需要 GC
  • 顺便说一句,您的同步(最后的 while 循环)很糟糕并且消耗了大量 CPU。你应该改用信号量之类的东西
  • 我在 JMH 基准测试中得到的结果与您完全不同。在Github Gist 中查看。我的预期结果是 AtomicInteger 比同步更快。
  • 在信号量的情况下,您的主线程将被阻塞,直到所有任务完成。当它被阻塞时,它不会消耗任何 CPU 时间,而您的循环正在主动等待,这意味着主线程以快速的频率主动唤醒、睡眠、唤醒、睡眠等。跨度>

标签: java synchronized atomicinteger


【解决方案1】:

尽管你的测试用例有代码问题,让我们谈谈多线程问题本身。

如果您将THREAD_NUM 设置为一个小得多的数字,例如8 或4,您会发现您的AtomicCounterSynchronizedCounter 快一点。运行 1000 个线程将在 AtomicInteger 的 CAS(比较和交换)上花费两个 CPU,这导致它的运行速度比 synchronized 代码块慢。

为了证明这一点,你可以实现一个LongadderCounter

private class LongadderCounter extends Counter {

    @Override
    public void increment() {
        longadder.increment();
    }

    @Override
    public int getResult() {
        return longadder.intValue();
    }
}

您会发现LongadderCounter 的速度要快得多。 LongAdderAtomicIntegerConcurrentHashMapCollections.synchronizedMap(new HashMap&lt;&gt;()),它将计数器分成多个部分,并对每个部分进行 CAS 以缓解竞争条件。

【讨论】:

  • 我对 LongAdder 的速度感到惊讶! AtomicInteger 花费了 1/15 的时间。并感谢您的礼貌解释!
猜你喜欢
  • 2021-03-18
  • 2012-07-25
  • 1970-01-01
  • 2023-04-09
  • 2020-12-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多