据我所知,当尝试从多个线程执行相同的操作时,对 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
block 生成预期的输出,但我需要解释为什么不只使用原子变量生成预期的输出。
我的推理就像是 - 无论线程执行顺序如何,它都会在另一个线程递增并打印原子变量的值之前递增和打印。
最佳答案
实际顺序与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());
}
关于Java 并发与原子类,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49968404/