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来确保对关键部分的互斥访问,导致数据竞争和多线程应用中的难以捕捉的错误。