【问题标题】:When AtomicInteger is faster than synchronized当 AtomicInteger 比同步快时
【发布时间】:2021-03-18 06:11:46
【问题描述】:

我已经阅读了大量文章,其中说 AtomicInteger 类的工作速度比同步构造快。我对 AtomicInteger 和“同步”进行了一些测试,在我的测试中,同步比 AtomicInteger 快得多。我想了解发生了什么问题:我的测试类不正确或 AtomicInteger 在其他情况下工作得更快?

这是我的测试类:

    public class Main {

    public static void main(String[] args) throws InterruptedException
    {
        // creating tester "synchronized" class
        TesterSynchronized testSyn = new TesterSynchronized();

        // Creating 3 threads
        Thread thread1 = new Thread(testSyn);
        Thread thread2 = new Thread(testSyn);
        Thread thread3 = new Thread(testSyn);

        // start time
        long beforeSyn = System.currentTimeMillis();

        // start
        thread1.start();
        thread2.start();
        thread3.start();

        thread1.join();
        thread2.join();
        thread3.join();

        long afterSyn = System.currentTimeMillis();
        long delta = afterSyn - beforeSyn;

        System.out.println("Test synchronized: " + delta + " ms");

        // _______________________________________________________

        // creating tester "atomicInteger" class
        TesterAtomicInteger testAtomic = new TesterAtomicInteger();

        thread1 = new Thread(testAtomic);
        thread2 = new Thread(testAtomic);
        thread3 = new Thread(testAtomic);

        // start time
        long beforeAtomic = System.currentTimeMillis();

        // start
        thread1.start();
        thread2.start();
        thread3.start();

        thread1.join();
        thread2.join();
        thread3.join();

        long afterAtomic = System.currentTimeMillis();
        long deltaAtomic = afterAtomic - beforeAtomic;

        System.out.println("Test atomic integer: " + deltaAtomic + " ms");
    }
}

// Synchronized tester
class TesterSynchronized implements Runnable {
    public int integerValue = 0;

    public synchronized void run() {
        for (int i = 0; i < 1_000_000; i++)
            integerValue++;
    }
}

// AtomicInteger class tester
class TesterAtomicInteger implements Runnable {
    AtomicInteger atomicInteger = new AtomicInteger(0);

    public void run() {
        for (int i = 0; i < 1_000_000; i++)
            atomicInteger.incrementAndGet();
    }
}

测试参数:3个线程和1_000_000个增量; 结果:

测试同步:7 毫秒。测试原子整数:51 ms

我很高兴了解为什么会发生这种情况。

UPD 如果将同步方法更改为同步块,测试将是正确的。

// Synchronized tester
class TesterSynchronized implements Runnable {
    public int integerValue = 0;

    public void run() {
        for (int i = 0; i < 1_000_000; i++) {
            synchronized (this) {
                integerValue++;
            }
        }
    }
}

【问题讨论】:

  • 在您的同步测试器中,您正在获取一个锁,然后执行您的百万增量。在您的 AtomicInteger 示例中,您为每个增量获取和释放一个锁。
  • 尝试将synchronized 包裹在integerValue++AtomicInteger 周围会更快。您无法比较这两种方法。
  • 确实是个问题!改成同步块后测试结果为:Test synchronized: 244 ms Test atomic integer: 95 ms。
  • 除了synchronized的错误,你应该看看correct benchmarking in Java是怎么做的。在这个测试中,synchronized 可能很慢,但在其他一些测试中,您希望避免影响结果的噪音。
  • 原来的TesterSynchronized循环可以被编译器优化。执行那个循环是没有意义的。它可以立即将 1_000_000 分配给 integerValue 而不是进行 1M 增量。

标签: java concurrency synchronization volatile atomicinteger


【解决方案1】:

您的代码中的明显区别在于AtomicIntger 版本允许交错具有任何访问权限的线程,而synchronized 版本则依次执行每个线程的整个循环。

可能还有其他问题。例如,JVM 可以合并一个synchronized 块的多个调用。根据平台的不同,incrementAndGet 可能不是原子操作,而是作为 CAS 循环实现 - 如果争用很高,这可能是个问题(我对此并不完全确定)。

无论以哪种方式排列,如果您有多个线程同时修改同一个内存位置,它都不会很快。

【讨论】:

  • “如果争用很高,这可能是个问题(我对此并不完全确定)”,相反:当争用很高时,CAS 特别好,因为它不会强制线程被屏蔽。
  • @akuzminykh 我认为问题在于多个并发线程可以循环,因为一个线程成功了。诚然,如果操作只是一个增量,那么这不太可能,并且它很可能通过锁定的加载-修改-存储来实现。
猜你喜欢
  • 2015-12-02
  • 2012-07-25
  • 1970-01-01
  • 1970-01-01
  • 2020-12-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多