编程后端开发者

在Java中,'volatile'变量是什么,它与'synchronized'有什么区别?什么时候使用volatile,为什么?

用 Hintsage AI 助手通过面试

答案

volatile是一个变量修饰符,确保所有线程可以看到此变量的更改。如果变量声明为volatile,则它的读取和写入直接来自主内存,而绕过线程的本地缓存。这防止了在线程中本地缓存值。

synchronized是一个关键字,它不仅确保可见性,还确保互斥性:在任何时刻,对于一个对象,只有一个线程可以执行synchronized块。

应在以下情况下使用volatile变量,适用于简单的标志和计数器:

  • 变量的值不依赖于先前的状态(例如,计数器只被写入一次,多次读取);
  • 不需要更复杂的操作顺序(检查后再行动)。

使用volatile的示例:

class Flag { private volatile boolean running = true; public void stop() { running = false; } public void loop() { while (running) { // ... } } }

设置陷阱的问题

可以使用volatile来确保变量递增的原子性吗?

答案: 不可以,volatile并不确保操作的原子性。例如,counter++即使counter声明为volatile也不是原子操作,因为该操作包括读取、修改和写入(多个步骤),可能会被其他线程中断。原子性可以通过synchronized或java.util.concurrent.atomic包中的类来确保。

示例:

class Counter { private volatile int count = 0; public void increment() { count++; // 不是原子操作! } }

由于对这一主题细节的无知而导致的真实错误示例


故事

在在线商店项目中,订单处理线程的完成标志是没有使用volatile实现的。结果,一个线程在无限循环中"卡住",因为它没有看到来自另一个线程的变量更改。诊断花费了几天时间。


故事

在金融系统中,开发者使用volatile int作为操作计数器。在高峰负载下,操作数量开始"丢失"。原因是在复杂的递增操作中失去了原子性。


故事

开发者混淆了volatile和synchronized,试图使用volatile来确保对关键部分的互斥访问,导致数据竞争和多线程应用中的难以捕捉的错误。