原子类的Java并发

原子类的Java并发,java,multithreading,synchronization,atomic,java-threads,Java,Multithreading,Synchronization,Atomic,Java Threads,据我所知,当试图从多个线程执行同一操作时,Java并发API的原子类上的操作会一个接一个地执行,以下程序的输出对我来说似乎不一致 public class VisitorCounterAtomic { private AtomicInteger visitorCount = new AtomicInteger(0); public void visitAndPrint() { System.out.println("Total Visitors: " + vi

据我所知,当试图从多个线程执行同一操作时,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
我知道我可以通过使用
同步
块来生成预期的输出,但是我需要一个解释,为什么不只使用原子变量来生成预期的输出


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

实际的顺序与
AtomicInteger

AtomicInteger
保证可以自动更新该值。它不能保证线程是按顺序执行的。
实际上,
ExecutorService
实例以异步方式处理任务。
因此,您不能有可预测的任务完成顺序。
实际上,在执行
incrementAndGet()
println()
之间有一个竞争条件:

例如,假设:

  • 线程A执行
    visitorCount.incrementAndGet()
    (计数器=1),但不执行
    println()
  • 线程A暂停
  • 线程B执行
    visitorCount.incrementAndGet()
    (计数器=2)和
    println()
  • 线程A已恢复<执行代码>打印项次()
结果:

访客总数:2

访客总数:1

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

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

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


要解决此问题,可以使用Syncronized函数来解决此问题。

visitorCount.incrementAndGet()
的调用按顺序执行。然而,线程的启动不能保证以任何特定的顺序进行。当调用线程的
start()
方法时,该线程的Runnable将在稍后执行。操作系统依赖于什么时候。我得到了你的解释,尽管在我发布问题之前我自己也考虑过这一点。我需要其他人的澄清。因此,主要原因是线程暂停/恢复和incrementAndGet()的原子操作不会阻止以无序方式使用System.out.println打印值。谢谢
public synchronized void visitAndPrint() {        
    System.out.println("Total Visitors: " + visitorCount.incrementAndGet());
}