volatile 用与不用的区别,适用哪些场景
/** * volatile 使得变量在各线程之间实时共享 * 是为了解决内存一致性问题 * * Using volatile variables reduces the risk of memory consistency errors, * because any write to a volatile variable establishes a happens-before relationship with subsequent reads of that same variable. * This means that changes to a volatile variable are always visible to other threads. * What's more, it also means that when a thread reads a volatile variable, * it sees not just the latest change to the volatile, * but also the side effects of the code that led up the change. * * @see https://docs.oracle.com/javase/tutorial/essential/concurrency/atomic.html * @author witas * */ public class Testvolatile { // 不加volatile则无法结束工作 volatile static int i = 0; public static void work() { System.out.println("开始工作"); while (i == 0) { } System.out.println("工作结束"); } public static void main(String[] args) { new Thread(() -> Testvolatile.work()).start(); try { Thread.sleep(1000L); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("通知下班"); Testvolatile.i = 1; try { Thread.sleep(1000L); } catch (InterruptedException e) { e.printStackTrace(); } } }
解决的是线程间变量实时共享,并不解决同步问题。它的作用是改变变量的访问方式,这里涉及到register (寄存器)
Declaring a Variable Volatile
volatile is a keyword that must be used when declaring any variable that will reference a device register. If this is not done, the compile-time optimizer might optimize important accesses away. This is important; neglecting to use volatile can result in bugs that are difficult to track down.
The correct use of volatile is necessary to prevent elusive bugs. It instructs the compiler to use exact semantics for the declared objects—in particular, to not optimize away or reorder accesses to the object. There are two instances where device drivers must use the volatile qualifier:
-
When data refers to an external hardware device register (memory that has side effects other than just storage). Note, however, that if the DDI data access functions are used to access device registers, it is not necessary to use volatile.
-
When data refers to global memory that is accessible by more than one thread, that is not protected by locks, and that relies on the sequencing of memory accesses. Using volatile is less expensive than using lock.
The following example uses volatile. A busy flag is used to prevent a thread from continuing while the device is busy and the flag is not protected by a lock:
while (busy) { /* do something else */ }
The testing thread will continue when another thread turns off the busy flag:
busy = 0;
However, since busy is accessed frequently in the testing thread, the compiler may optimize the test by placing the value of busy in a register, then test the contents of the register without reading the value of busy in memory before every test. The testing thread would never see busy change and the other thread would only change the value of busy in memory, resulting in deadlock. Declaring the busy flag as volatile forces its value to be read before each test.
Note –
It would probably be preferable to use a condition variable, discussed under Condition Variables in Thread Synchronization rather than the busy flag in this example.
It is also recommended that the volatile qualifier be used in such a way as to avoid the risk of accidental omission. For example, this code
struct device_reg { volatile uint8_t csr; volatile uint8_t data; }; struct device_reg *regp;
is recommended over:
struct device_reg { uint8_t csr; uint8_t data; }; volatile struct device_reg *regp;
Although the two examples are functionally equivalent, the second one requires the writer to ensure that volatile is used in every declaration of type struct device_reg. The first example results in the data being treated as volatile in all declarations and is therefore preferred. Note as mentioned above, that the use of the DDI data access functions to access device registers makes it unnecessary to qualify variables as volatile.
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)