【问题标题】:Does a non-volatile variable need synchronized?非易失性变量是否需要同步?
【发布时间】:2020-03-27 23:42:22
【问题描述】:
考虑 2 个线程和一个数组 int[] values。
第一个线程正在执行:
synchronized (values) {
values[i] = 58;
}
当第二个线程正在执行时:
if (values[i] == 58) {
}
在synchronized 块之外。
如果第一个线程首先执行values[i]= 58,是否保证如果第二个线程稍晚执行,即使第二个线程在@外读取values[i],第二个线程的if也会读取58 987654331@块?
【问题讨论】:
标签:
java
concurrency
synchronized
【解决方案1】:
如果第一个线程先执行values[i]= 58,是否保证
如果第二个线程稍晚执行,则 if 的
即使第二个线程读取值[i],第二个线程也会读取 58
在同步块之外?
没有
以这种方式同步不会停止其他线程同时对数组执行任何操作。但是,将阻止其他线程对数组进行锁定。
【解决方案2】:
不保证上述行为。这种“可见性”的保证实际上是happens-before关系的主题:
避免内存一致性错误的关键是理解happens-before relationship。这种关系只是保证一个特定语句的内存写入对另一个特定语句可见。
Happens-before 关系 (according to JLS) 可以这样实现:
- 线程中的每个动作都发生在该线程中的每个动作之前
- 监视器的解锁(同步块或方法退出)发生在同一监视器的每个后续锁定(同步块或方法入口)之前。并且由于happens-before关系是可传递的,因此线程在解锁之前的所有操作都发生在任何线程锁定该监视器之后的所有操作之前。
- 对易失性字段的写入发生在对同一字段的每次后续读取之前。 volatile 字段的写入和读取具有与进入和退出监视器类似的内存一致性效果,但不需要互斥锁定。
- 在线程上启动的调用发生在已启动线程中的任何操作之前。
- 线程中的所有操作都发生在任何其他线程从该线程的连接成功返回之前。
因此,在您的特定情况下,您实际上需要使用共享监视器或AtomicIntegerArray 进行同步,以便对数组进行线程安全访问; volatile 修饰符将无济于事,因为它只影响指向数组的变量,而不影响数组的元素 (more detailed explanation)。