据我所知,当尝试从多个线程执行同一操作时,对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());
}