多线程场景下Java类的getValue是否需加synchronized?volatile是否必要?
问题分析与解答
首先给出你提交的代码示例:
public class A { private volatile int value = 0; // is volatile needed here? synchronized public void increment() { value++; // Atomic is better, agree } public int getValue() { // synchronized needed ? return value; } }
核心结论
- 如果你保持
getValue()不加synchronized的现有写法:value的volatile修饰符必须保留,不需要额外给getValue()加synchronized - 如果你给
getValue()加上synchronized:value的volatile修饰符可以去掉,不需要额外保留
原理说明
Java内存模型(JMM)的可见性规则要求:一个线程对共享变量的修改,必须能被其他线程及时读取到最新值,否则会出现脏读问题。
- 为什么无锁
getValue()需要volatilesynchronized的可见性保证只作用于加了同一把锁的同步块:increment()加了锁,退出同步块时会把value的修改刷新到主存,但无锁的getValue()不会主动从主存拉取最新值,可能会一直读到线程工作内存里缓存的旧值。而volatile修饰的变量会强制所有读操作直接从主存读取、写操作立即刷新回主存,刚好补上了这个可见性缺口。 - 为什么加锁
getValue()不需要volatile
如果getValue()也加了synchronized,那么读写方法都持有同一个A类实例的对象锁,根据Happens-Before规则:同一个锁的解锁操作,优先于后续对这个锁的加锁操作。所以increment()对value的修改,一定对后续拿到同一把锁执行的getValue()可见,不需要volatile额外保证可见性。
更优实现方案
建议直接用java.util.concurrent.atomic.AtomicInteger替代volatile int加synchronized的组合,高并发场景下性能更好,也不用手动处理同步逻辑:
public class A { private AtomicInteger value = new AtomicInteger(0); public void increment() { value.incrementAndGet(); } public int getValue() { return value.get(); } }
内容的提问来源于stack exchange,提问作者J.J. Beam
相关产品推荐
相关产品推荐

