Java并发与原子类

据我所知,当尝试从多个线程执行同一操作时,对Java并发API的Atomic类的操作是一个接一个地执行的,以下程序的输出对我而言似乎不一致.

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实例以异步方式处理任务.
因此,您无法获得可预测的任务完成顺序.
实际上,在递增AndGet()和println()之间有一个竞争条件:

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

例如,假设:

>线程A执行visitorCount.incrementAndGet()(counter = 1)但不执行println()
>线程A已暂停
>线程B执行visitorCount.incrementAndGet()(counter = 2)和println()
>线程A恢复. println()被执行

结果:

Total Visitors: 2

Total Visitors: 1

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

public synchronized void visitAndPrint() {        
    System.out.println("Total Visitors: " + visitorCount.incrementAndGet());
}
上一篇:java-如何确保我不在两个线程之间同时共享同一套接字?


下一篇:如何在Android上与SyncAdapter同步?