社招面试避坑指南:volatile和Atomic的正确使用姿势!

软件求生 2025-03-04 08:19:49



小米最近在准备社招面试,毕竟在互联网这条“卷”路上,稍微停滞不前,就可能被更年轻的程序员们弯道超车。为了在面试中脱颖而出,小米最近疯狂刷面试题,尤其是多线程相关的知识。

这天,小米和面试官约了一场线上面试,一上来面试官就丢出了一个高频问题:

“Java 中 volatile 变量和 Atomic 变量有什么不同?”

小米心里一紧,心想:“这不就是面试八股文里常见的问题吗?” 于是,他开始了自信的作答……

volatile 变量:能见度好,但不保证原子性

1. volatile 是什么?

小米微微一笑,说道:

volatile 关键字的作用是保证变量的可见性,防止指令重排序。

通俗点说,Java 的 volatile 变量类似于“全员广播”模式。每当某个线程修改了这个变量,其他线程都能立刻看到最新的值,不会出现“脏数据”。

2. volatile 如何保证可见性?

JMM(Java 内存模型)规定,volatile 变量在写入时,会多做一步操作:

普通变量赋值:线程先从主内存中读取数据,复制到自己的工作内存,修改后再写回主内存,其他线程无法感知这个变化。

volatile 变量赋值:直接刷新到主内存,并让所有线程的缓存失效,从而保证所有线程都能立即看到最新的值。

就像是小米早上给群发了一条消息:“今天团建改到 3 点!” 这样,所有人都能立即收到,而不会有人还在按照原来的时间去行动。

3. 但 volatile 不能保证原子性

面试官点了点头,然后笑着问:“那 volatile 变量能保证原子性吗?”

小米立刻摇头:“不能!”

他举了个例子:

看上去 count++ 只是一个简单的自增操作,但实际上它是三步操作:

读取 count 的值

执行 +1 操作

将新值写回 count

如果有两个线程 A 和 B 同时执行 increment() 方法:

A 读取 count=0,执行 count+1,还没来得及写回

B 也读取 count=0,执行 count+1,然后写回

A 最后写回 count=1

本来执行两次 increment(),结果 count 还是 1,丢失了一次更新!这就是“并发问题”。

所以,volatile 仅仅保证了变量的可见性,并不能保证操作的原子性!

Atomic 变量:天生线程安全,保证原子性

小米接着说道:“如果想保证原子性,Java 提供了 java.util.concurrent.atomic 包,里面的 Atomic 变量是更好的选择。”

1. 什么是 Atomic 变量?

Atomic 变量依靠CAS(Compare-And-Swap)机制来保证原子性。

还是刚才的例子,如果我们改用 AtomicInteger:

这样,多个线程同时调用 increment() 方法时,就不会丢失数据了。

2. Atomic 如何保证原子性?

Atomic 变量的核心是CAS(比较并交换,Compare-And-Swap),它的原理是:

读取旧值 oldValue

计算新值 newValue

使用 CPU 指令尝试将 oldValue 更新为 newValue

如果 oldValue 还是旧值,就更新成功;如果已经被其他线程修改了,就重新尝试。

这种方式避免了传统的锁(synchronized)带来的性能问题,CAS 是无锁并发的基础,比 synchronized 更高效!

不过,CAS 也不是万能的,它可能导致“ABA 问题”(变量值在中间被修改了,但最终又变回去了),解决方案是AtomicStampedReference这种带版本号的原子变量。

volatile vs Atomic:到底该选谁?

面试官看小米讲得这么清楚,问道:“所以,在实际开发中,volatile 和 Atomic 该怎么选呢?”

小米总结道:

如果只是需要保证变量的可见性,而不涉及并发修改,可以使用 volatile,比如:

线程间的标志位

double check 机制的 instance 变量

如果需要保证变量的原子性,应该使用 Atomic 变量,比如:

计数器(AtomicInteger)

线程安全的累加器(LongAdder)

面试官的加分题

面试官满意地点了点头,然后问:“那 synchronized 和 Atomic 有什么不同呢?”

小米心想:“这不就是送分题吗?”

他笑着回答:

synchronized 是一种阻塞式的同步机制,线程会进入阻塞状态,性能开销较大。

Atomic 变量是非阻塞的,它基于 CAS 操作,不会让线程进入阻塞状态,因此更高效。

synchronized 适合需要多个变量一起同步的场景,而 Atomic 适合单个变量的无锁操作。

面试官点头:“不错,今天的面试到这里,回去等通知吧!”

END

小米挂断电话,感觉这次面试发挥得不错,心里暗自高兴。为了帮助更多正在找工作的朋友,他在朋友圈里分享了今天的面试知识点:

volatile 只保证可见性,不保证原子性

Atomic 变量使用 CAS 机制,保证原子性,适用于计数、累加等操作

synchronized 适用于多个变量的同步,Atomic 适用于单个变量的高效无锁操作

朋友们纷纷点赞,甚至有好几个私聊他要面经。小米想了想,决定再刷一遍八股文,争取在下一场面试中更进一步!

公众号对技术型文章的推送机制有所调整,需要大家多多点赞在看转发收藏,才能让更多技术同行们能看到优质的技术分享~

我是小米,一个喜欢分享技术的31岁程序员。如果你喜欢我的文章,欢迎关注我的微信公众号“软件求生”,获取更多技术干货!

0 阅读:0

软件求生

简介:从事软件开发,分享“技术”、“运营”、“产品”等。