线程同步块未退出时,其他线程读变量是否需加volatile?
现有类A的代码如下:
class A { int var; void method() { read(var); synchronized (this) { // 在这里更新var的值 var = ...; } } }
假设场景:线程1获取锁进入同步块,更新了var的值(该值可能缓存于处理器核心中),且线程1仍未退出同步块。此时线程2启动并进入method()读取var的值,请问:
- 线程2必然能获取到更新后的值吗?
- synchronized是否能保证线程2获取到更新值?
- 这种情况下是否需要将var声明为volatile?
注:所有操作针对同一对象。
线程2不一定能获取到更新后的值
线程1在同步块内更新var后,只要没退出同步块,就不会触发synchronized的内存可见性语义——只有线程退出同步块时,才会把工作内存中的修改刷新到主内存;其他线程进入同一个锁的同步块时,才会强制从主内存读取最新值。而线程2的读操作在同步块外,既没有获取锁的动作,也不会触发主内存读取,所以它读到的可能是自己工作内存里的旧值,或者主内存中未被线程1刷新的旧值。synchronized不能保证线程2获取到更新值
synchronized的内存可见性规则是:线程退出同步块时的修改,对后续进入该同步块的线程可见。但线程2的读操作不在同步块内,且线程1还没退出同步块,所以synchronized的可见性机制在这里不生效。线程1的修改还停留在自己的工作内存中,没有同步到主内存,线程2无法保证读到最新值。这种情况下需要将var声明为volatile
如果希望线程2在同步块外读取var时,能看到线程1在同步块内的实时修改,就需要给var加上volatile修饰。volatile会强制所有线程直接从主内存读写变量,禁止线程缓存变量副本,这样线程1对var的修改会立即刷新到主内存,线程2读取时也会直接获取最新值。当然,如果线程2的读操作也放到同一个同步块内,不加volatile也能保证可见性,但如果读操作必须在同步块外,volatile就是必要的。
内容的提问来源于stack exchange,提问作者user3790597

