在努力了解通过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可见一斑。
我有我失去了一些东西很明显的感觉。
当它出现的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());
}
如果您编辑您的问题解释为什么你需要的输出是连续的,也许有更好的解决方案,没有这种竞争情况。
您需要将整个建筑为同步:
synchronized(this) {
int counterValue = this.counter.incrementAndGet();
System.out.println(this.threadName + ": " + counterValue);
}
在这种情况下,虽然你没有使用的AtomicInteger。 平原int
将工作(计数器++)。
按顺序打印时,incAndGet和的println必须都在临界区域 ,一块代码只有一个线程可以进入,其他被阻塞。 与可实现的二进制信号,例如,像Java synchronized
。
你可以把头上的事情,有一个线程递增计数器和打印。 其他线程可以在“关键区域”只拿了一个又一个的柜台。 这将是更有效,因为关键区域应该保持小而最好做任何I / O。