一篇文章彻底搞懂synchronized和volatile,看完涨薪50%!

软件求生 2025-03-07 08:28:14



小米是一家互联网大厂的Java开发工程师,最近在准备面试题。他的朋友小明在另一家公司面试Java后端工程师,面试回来一脸生无可恋:“面试官上来就问synchronized 和 volatile 的区别,我结巴了半天,最后被怼得体无完肤!” 小米哈哈大笑:“这个问题很好回答啊,我来给你讲讲。” 于是,一场关于 Java 并发的讨论就此展开……

synchronized 和 volatile 的基本概念

小米清了清嗓子,开始讲解:

“我们写 Java 代码时,多线程编程是绕不开的一个话题,而 synchronized 和 volatile 这两个关键字,都是 Java 提供的线程同步手段。”

1、synchronized:独占锁,保证原子性和可见性

synchronized 是 Java 提供的关键字,它表示只有一个线程可以获取作用对象的锁,进入同步代码块,其他线程必须等待。

这样可以防止多个线程并发修改共享资源,保证变量的可见性和原子性,防止线程安全问题。

2、volatile:内存可见性,防止指令重排序

volatile 关键字用于修饰变量,它告诉 CPU 和编译器:

这个变量不能被缓存,必须从主存(内存)中读取。

禁止指令重排序,防止CPU对代码执行顺序进行优化,导致多线程环境下的程序异常。

小明点点头:“听起来好像都能用来解决并发问题啊?它们的区别到底在哪?”

synchronized 和 volatile 的核心区别

小米笑着说:“这正是面试官的坑点!虽然它们都跟线程安全有关,但作用完全不同。”

1、修饰对象的范围不同

小米举例:“比如你有一个 count 变量,你可以用 volatile 让它的修改对所有线程可见。但如果你要保证 count++ 这个操作的原子性,那 volatile 就不行了,必须用 synchronized。”

2、是否保证原子性

小明皱眉:“等等,那如果 volatile 不能保证原子性,它的用武之地在哪?”

小米解释:“volatile 适用于单一变量的状态标记,比如双重检查锁(DCL)模式下的 instance 变量,或者是 boolean flag 这样的简单开关变量。而 synchronized 适用于复杂逻辑操作,比如 count++ 这种需要原子性保护的操作。”

synchronized 和 volatile 的其他区别

小米继续深入讲解:

1、是否会造成线程阻塞

2、是否会被编译器优化

小明惊讶:“原来 Java 还这么智能,synchronized 还能被优化?”

小米点头:“对的!Java 1.6 以后,synchronized 进行了很多优化,比如:

偏向锁:如果一个线程一直在使用同一个锁,JVM 就不会频繁地加锁和释放锁。

轻量级锁:多个线程尝试竞争锁时,不会立即进入阻塞状态,而是使用 CAS 方式尝试加锁,提高性能。

锁消除、锁膨胀:JVM 会根据实际情况优化锁的使用。”

面试中如何回答 synchronized 和 volatile 的区别

小米总结了一套“黄金答题模板”:

“如果面试官问 synchronized 和 volatile 的区别,你可以这么答:

volatile只能修饰变量,synchronized 既能修饰变量,也能修饰方法和代码块。

volatile保证变量的可见性,但不保证原子性;synchronized同时保证可见性和原子性。

volatile不会造成线程阻塞,而 synchronized 可能会导致线程阻塞。

volatile不能被编译器优化,而 synchronized 通过 JVM 的优化(如偏向锁、轻量级锁)能提高性能。

volatile 适用于状态标记等简单场景,而 synchronized 适用于临界区保护、多个操作组合的场景。”

小明眼睛一亮:“听你这么一说,我感觉能答出个八九不离十了!”

真实场景下的选择

小米最后补充道:“不过,面试官可能还会问你——在真实项目中该怎么选?你可以告诉他:”

如果只是想让变量的修改对所有线程可见,且不涉及复合操作(如count++),可以用 volatile。

如果需要保证线程安全,操作需要原子性,那就用 synchronized(或者 Lock 机制)。

如果涉及高并发,还可以考虑 ReentrantLock,它比 synchronized 更灵活。

面试官的 “坑” 及应对

小米最后提醒:“面试官喜欢挖坑,比如问你:”

1、volatile 适用于哪些场景?

适用于状态标记(比如 boolean flag),单例模式的双重检查锁(DCL)。

2、为什么 volatile 不能保证原子性?

因为 volatile 只是保证了线程可见性,但 count++ 这样的操作是读取-计算-写入,中间有多个步骤,volatile 无法保证其不被其他线程干扰。

3、为什么 synchronized 能保证原子性?

因为 synchronized 会让线程独占锁,保证操作的完整性,其他线程必须等待锁释放。

END

讲完这一切,小米拍拍小明的肩膀:“放心去面试吧,下次面试官再问,你就把这一套直接甩过去!”

小明哈哈大笑:“这次我一定能拿下!”

你学会了吗?如果你在面试中遇到这个问题,记住:volatile 适用于简单变量可见性,synchronized 适用于多线程同步和原子性操作。祝你面试成功!

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

0 阅读:0
软件求生

软件求生

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