使用的AtomicInteger为静态共享计数器(Using AtomicInteger as a

2019-09-16 10:37发布

在努力了解通过Java同步,我只是瞎搞与像创建线程之间共享的计数器一些简单的事情。

我碰到的问题是,我无法弄清楚如何顺序打印计数器的时间100%。

int counterValue = this.counter.incrementAndGet();
System.out.println(this.threadName + ": " + counterValue);

以上递增AtomicInteger counter ,获取新的价值,并将其打印到由负责该更新线程名称标识的控制台。 当它出现了,会出现问题incrementAndGet()方法打印当前线程的更新值之前会引起JVM在上下文切换到另一个线程以获取更新。 这意味着该值被递增1,但不打印,直到该线程返回到执行状态。 看着这个例子的输出时,这是很明显的:

Thread 3: 4034
Thread 3: 4035
Thread 3: 4036
Thread 1: 3944
Thread 1: 4037
Thread 1: 4039
Thread 1: 4040
Thread 2: 3863
Thread 1: 4041
Thread 1: 4043

你可以看到,当执行返回到线程1,它打印的价值,并继续更新。 同样是与线程2可见一斑。

我有我失去了一些东西很明显的感觉。

Answer 1:

当它出现的incrementAndGet()方法会引起JVM在上下文切换到另一个线程其更新打印当前线程的更新值之前,会出现问题

这是一个经常可以在这些情况下可能发生的竞争条件。 虽然AtomicInteger计数器被正确递增,有什么可以阻止Thread 2被换出的增量情况和之前 之后 println被调用。

int counterValue = this.counter.incrementAndGet();
// there is nothing stopping a context switch here
System.out.println(this.threadName + ": " + counterValue);

如果您想要打印的“反顺序100%的时间”你将不得不围绕增量两个锁同步println调用。 当然,如果你这样做,那么AtomicInteger被浪费。

synchronized (counter) {
    System.out.println(this.threadName + ": " + counter.incrementAndGet());
}

如果您编辑您的问题解释为什么你需要的输出是连续的,也许有更好的解决方案,没有这种竞争情况。



Answer 2:

您需要将整个建筑为同步:

synchronized(this) {    
   int counterValue = this.counter.incrementAndGet();
   System.out.println(this.threadName + ": " + counterValue);
}

在这种情况下,虽然你没有使用的AtomicInteger。 平原int将工作(计数器++)。



Answer 3:

按顺序打印时,incAndGet和的println必须都在临界区域 ,一块代码只有一个线程可以进入,其他被阻塞。 与可实现的二进制信号,例如,像Java synchronized

你可以把头上的事情,有一个线程递增计数器和打印。 其他线程可以在“关键区域”只拿了一个又一个的柜台。 这将是更有效,因为关键区域应该保持小而最好做任何I / O。



文章来源: Using AtomicInteger as a static shared counter