原子类的Java并发
据我所知,当试图从多个线程执行同一操作时,Java并发API的原子类上的操作会一个接一个地执行,以下程序的输出对我来说似乎不一致原子类的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
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执行
(计数器=1),但不执行visitorCount.incrementAndGet()
println()
- 线程A暂停
- 线程B执行
(计数器=2)和visitorCount.incrementAndGet()
println()
- 线程A已恢复<执行代码>打印项次()
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());
}