我有以下代码
class VolatileCount {
volatile int count;
Object lock = new Object();
public void increment() {
synchronized (lock) {
count = count + 1;
}
System.out.print(" " + count);
}
}
如果我从多个线程调用increment()
相同的对象,我会得到以下输出(可能在您的机器上有所不同)
2 3 2 5 4 8 8 6 11 13 10 9 15 14 12 20 19
看着重复的数字,我认为发生 - 之前似乎被打破了,因为考虑前三个数字(2 3 2),如果一个线程看到3,则发生增量,因为变量是volatile,其值应为3或更大,但在任何线程中都不能为2 但是,打印线似乎已在这里重新排序,重新排序该行是否正确?我在这里错过了什么?我在JDK 7(Eclipse)上运行
答案 0 :(得分:7)
<强>更新强>
因为您似乎想要解释“2 3 2”的具体情况
i
(现在为1)i
(现在为2)i
(X加载2)i
(Y加载2)i
,读取i
,打印i
(打印3)重点是:System.out.print(" " + count)
不是原子的。执行易失性读取之后和打印值之前,线程可以被抢占。
如果您想防止打印重复值,您必须执行锁定中的易失性读取:
public void increment() {
int localCount;
synchronized (lock) {
count = count + 1;
localCount = count; // volatile load
}
System.out.print(" " + localCount);
}
但这不会阻止值无序打印。为了按顺序打印它们,没有重复,您还必须将print
移动到锁中。
print语句在锁外。
考虑在 System.out.print(" " + count)
内运行的代码。
i
print
参数并对count
变量执行易失性读取,并加载值2
。i
i
(现在是3
),调用运行完成的print方法。print
完成,打印2。这会使数字显示乱序,例如“3 2 4”。
在以下情况下也可能会重复某些数字:
i
(现在为2)i
(现在为3)i
(即3)i
(即3)答案 1 :(得分:2)
考虑2个主题。第一个线程获得锁定,递增计数到1
并释放锁定。第二个线程获得锁定,增量计数为2
,释放锁定,打印2,然后第一个线程获取count(现在为2
)值以打印它。在这种情况下,您将获得2 2
特别针对您的示例:
你有2 3 2
等...
答案 2 :(得分:1)
问题可能出在System.out.print
多线程上
检查this discussion 并尝试从命令行运行它,因为您的IDE可能会缓冲输出
如果您将打印件放入锁内,则应解决问题
答案 3 :(得分:0)
您的打印输出位于受保护区域之外。这意味着您可能会在增量和打印之间的任何时间被中断,包括在获取计数之后但在将其发送到System.out之前。如果你在同步块中移动它,我相信你会看到你期望的行为。
易失性不是同步的替代品。它只是警告编译器该值可能随时发生变化,因此应在每次引用时取出而不是优化出来。