使用原子类进行Java并发处理

4
据我所知,当尝试从多个线程执行相同的操作时,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

我知道我可以通过使用同步块来生成我期望的输出,但我需要解释为什么不仅使用原子变量就能生成期望的输出。
我的推理是这样的-无论线程执行的顺序如何,它都会在另一个线程递增和打印原子变量值之前递增和打印。

visitorCount.incrementAndGet() 的调用是按顺序执行的。然而,线程的启动并不保证以任何特定的顺序发生。当调用线程的 start() 方法时,该线程的 Runnable 将在稍后的某个时间执行。确切的时间取决于操作系统。 - VGR
2个回答

4
实际顺序与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()
结果为:

总访问量:2

总访问量:1

通过同步方法,您应该按预期顺序进行:
public synchronized void visitAndPrint() {        
    System.out.println("Total Visitors: " + visitorCount.incrementAndGet());
}

尽管在发布问题之前我自己也考虑过这个问题,但我理解了你的解释。我需要他人对此进行澄清。因此,主要的原因是线程的暂停/恢复以及使用incrementAndGet()方法的原子操作无法按顺序地使用System.out.println输出值。谢谢。 - iamcrypticcoder

-1

AtomicInteger类提供了原子操作。这意味着它将确保只有一个线程在该变量上执行操作。它的性质是非阻塞的。在您的代码中,多个线程同时运行,因此AtomicInteger正在很好地完成其工作,但哪个线程将首先完成其任务并不取决于AtomicInteger

要解决此问题,您可以使用同步函数来解决此问题。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接