Java并发场景下Volatile+Synchronized与全Synchronized实现选型咨询
两种实现方案对比结论
第一种volatile+仅flip方法加synchronized的方案存在竞态条件,无法满足多线程并发安全要求;第二种所有方法加synchronized的方案可以满足线程安全要求,是更优的选择。
方案1:Volatile+仅原子方法加Synchronized
public class CalcStrategySet { private volatile CalcStrategy current; private volatile CalcStrategy backup; private volatile boolean isBackup; public CalcStrategySet(CalcStrategy current, CalcStrategy backup) { this.current = current; this.backup = backup; } public void isStandard() { return !isBackup; } public void merge(CalcStrategy other) { current.merge(other); } public synchronized void flip() { if(!isBackup) { current = backup; backup = null; isBackup = true; } else { throw new IllegalStateException("Already in backup mode"); } } }
存在的问题
merge方法无同步保护:volatile只能保证current引用本身的可见性,无法保证current.merge(other)内部操作的原子性和可见性。如果CalcStrategy的merge方法本身没有做线程安全处理,多线程同时调用merge修改同一个策略实例的状态时,必然会出现数据错乱。- 复合操作存在竞态:如果业务逻辑先调用
isStandard()判断当前模式,再执行对应操作,两步操作之间没有锁保护,判断完成后可能被其他线程执行flip切换模式,导致后续操作和判断结果不匹配。 flip和merge并发冲突:线程刚读取到current引用还没执行merge操作时,其他线程可能已经执行完flip替换了current指向的实例,最终merge操作落到了已失效的旧策略对象上,不符合业务预期。
方案2:全Synchronized实现
public class CalcStrategySet { private CalcStrategy current; private CalcStrategy backup; private boolean isBackup; public CalcStrategySet(CalcStrategy current, CalcStrategy backup) { this.current = current; this.backup = backup; } public synchronized void isStandard() { return !isBackup; } public synchronized void merge(CalcStrategy other) { current.merge(other); } public synchronized void flip() { if(!isBackup) { current = backup; backup = null; isBackup = true; } else { throw new IllegalStateException("Already in backup mode"); } } }
合理性说明
所有对外方法都加了synchronized修饰,以当前实例作为锁,保证同一时间只有一个线程执行类中的任意方法,完全规避了竞态问题:
- 执行
merge的过程中flip无法抢占锁执行,不会出现current中途被替换的问题 - 多线程调用
merge时会串行执行,避免了并发修改CalcStrategy状态的问题 - 不需要额外加volatile修饰成员变量,因为
synchronized已经同时保证了操作的原子性、可见性和有序性,所有对成员变量的修改都会同步到主内存,读操作也会从主内存拉取最新值。
可选优化方案
如果读操作(如isStandard)的调用频率远高于写操作(flip、merge),可以用ReentrantReadWriteLock读写锁替代synchronized:读方法加共享读锁,支持多线程并发读;写方法加独占写锁,同一时间只有一个线程执行写操作,相比全synchronized的串行执行效率更高。
内容的提问来源于stack exchange,提问作者Ravi
相关产品推荐
相关产品推荐

