Java 与 Atomic 并发 Class

Java Concurrency with Atomic Class

据我所知,Java 并发 API 的原子 类 上的操作在尝试从多个线程执行相同操作时一个接一个地执行,以下程序的输出对我来说似乎不一致。

public class VisitorCounterAtomic {

    private AtomicInteger visitorCount = new AtomicInteger(0);

    public void visitAndPrint() {
        System.out.println("Total Visitors: " + visitorCount.incrementAndGet());
    }

    public static void main(String... args) {
        ExecutorService service = null;
        VisitorCounterAtomic counter = new VisitorCounterAtomic();

        try {
            service = Executors.newFixedThreadPool(20);
            for (int i = 0; i < 10; i++)
                service.submit(() -> counter.visitAndPrint());
        } finally {
            if (null != service) service.shutdown();
        }
    }
}

输出:

Total Visitors: 1
Total Visitors: 4
Total Visitors: 2
Total Visitors: 5
Total Visitors: 3
Total Visitors: 6
Total Visitors: 7
Total Visitors: 8
Total Visitors: 9
Total Visitors: 10

我的预期输出:

Total Visitors: 1
Total Visitors: 2
Total Visitors: 3
Total Visitors: 4
Total Visitors: 5
Total Visitors: 6
Total Visitors: 7
Total Visitors: 8
Total Visitors: 9
Total Visitors: 10

我知道我可以使用 synchronization 块生成预期的输出,但我需要解释为什么不只使用原子变量生成预期的输出。

我的推理是这样的——无论线程执行的顺序如何,它都会在另一个线程递增并打印原子变量的值之前递增和打印。

实际顺序与AtomicInteger没有任何关系。
AtomicInteger 保证值可以自动更新。它不保证线程按顺序执行。
实际上,ExecutorService 实例以异步方式处理任务。
所以你不能有一个可预测的任务完成顺序。
实际上你在 incrementAndGet()println():

的执行之间有一个竞争条件
public void visitAndPrint() {        
    System.out.println("Total Visitors: " + visitorCount.incrementAndGet());
}

例如假设:

  • 线程 A 执行 visitorCount.incrementAndGet()(计数器 = 1)但不执行 println()
  • 线程 A 暂停
  • 线程B执行visitorCount.incrementAndGet()(计数器=2)和println()
  • 线程 A 已恢复。 println() 执行

结果:

Total Visitors: 2

Total Visitors: 1

通过同步方法,您应该有预期的顺序:

public synchronized void visitAndPrint() {        
    System.out.println("Total Visitors: " + visitorCount.incrementAndGet());
}

AtomicInteger class 提供原子操作。这意味着它将确保一次只有一个线程对该变量执行操作。它在本质上也是非阻塞。在你的代码中,多个线程同时 运行 所以 AtomicInteger 正在做它的工作相当好但是哪个线程将首先完成它的任务它不依赖于 原子整数

为了解决这个问题,你可以使用Syncronized Function来解决这个问题。