结合Volatile与Synchronized能否实现可见性与原子性?两种方案对比
两种计数器方案的效果对比与分析
直接给结论:这两种方案功能完全等价,都能实现线程安全的计数器,第二种方案没有问题,甚至在读取性能上更优。
方案一的原理
方案一中,getCount和increment都通过synchronized(this)锁住当前对象:
synchronized既保证了同一时间只有一个线程能进入同步块(互斥性),还会在进入同步块时刷新主内存数据到线程缓存,退出同步块时把线程缓存的修改同步回主内存(可见性)。- 对于
counter++这种拆分成「读值-加1-写回」的非原子操作,同步块能确保这三步不会被其他线程打断,保证了操作的原子性。
方案二的原理
方案二用volatile修饰counter,同时给increment方法加synchronized:
volatile的核心作用是保证变量的可见性:每次读取counter时都会直接从主内存获取最新值,不会使用线程本地缓存;每次修改后也会立刻同步回主内存。但volatile不保证原子性,单独用它修饰counter的话,counter++会存在线程安全问题。- 而
increment方法的synchronized修饰符,刚好弥补了volatile的原子性缺陷——同一时间只有一个线程能执行这个方法,确保「读值-加1-写回」的完整流程不会被打断。同时synchronized退出时的内存同步机制,搭配volatile的可见性,能保证其他线程调用getCount时总能拿到最新的counter值。
为什么第二种方案没问题?
很多人习惯用第一种方案,可能是觉得「所有操作都加锁更稳妥」,但实际上方案二的设计更高效合理:
- 读取操作
getCount只需要保证可见性,不需要原子性,volatile完全能满足需求,省去了加锁带来的上下文切换开销。 - 写操作
increment需要原子性,用synchronized保证即可,结合volatile的可见性,整个计数器的读写都是线程安全的。
所以两种方案都能正确实现线程安全的计数器,在读取场景更多的业务中,第二种方案的性能表现会更优。
内容的提问来源于stack exchange,提问作者itspr
相关产品推荐
相关产品推荐

