Java中volatile的作用以及用法

本文探讨了Java中volatile关键字的作用原理,包括其确保变量读写的可见性而不保证操作的原子性。并通过示例说明了即使未使用volatile,线程间仍可能观察到变量的变化。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

找了很多资料,包括《Java并发编程实战》,综合一下各家的说法就是:

volatile让变量每次在使用的时候,都从主存中取。而不是从各个线程的“工作内存”。

volatile具有synchronized关键字的“可见性”,但是没有synchronized关键字的“并发正确性”,也就是说不保证线程执行的有序性。

也就是说,volatile变量对于每次使用,线程都能得到当前volatile变量的最新值。但是volatile变量并不保证并发的正确性。

看下面的例子:

假如count变量是volatile的。线程1,线程2 在进行read,load 操作中,发现主内存中count的值都是5,那么都会加载这个最新的值在线程1堆count进行修改之后,会write到主内存中,主内存中的count变量就会变为6,线程2由于已经进行read,load操作,在进行运算之后,也会更新主内存count的变量值为6,导致两个线程及时用volatile关键字修改之后,还是会存在并发的情况。

--------------------------------------

综合了查找到的资料,上面的解释,还算理解的过去。但是《java并发编程实战》上的例子,就不是很明白了。

看下面

----------------------------------------------------------------

java并发编程实战上说,如果变量不是volatile的,那么在被其他线程修改之后,之前的线程是不会感知到的。但是下面的代码,asleep被修改了之后,其他四个线程却都停止了输出。不明白是怎么回事。

[java]  view plain  copy
  1. package comz;  
  2.   
  3. /** 
  4.  * 这段代码虽然没有volatile,但是另外的线程设置为true的时候,其他的四个线程依然停止了运行。 与书上不一致。为啥? 
  5.  * 书上说,如果不是volatile的,则另外的线程更新这个值的时候,其他的线程是不会感知到的。所以其他线程就不会停止执行。 
  6.  * @author naughty 
  7.  *  
  8.  */  
  9. class T {  
  10.   
  11.     public static boolean asleep = false;  
  12.   
  13.     public static void main(String[] args) throws InterruptedException {  
  14.         for (int i = 0; i < 4; i++) {  
  15.             new Thread(new Runnable() {  
  16.                 @Override  
  17.                 public void run() {  
  18.   
  19.                     judge();  
  20.                 }  
  21.             }).start();  
  22.         }  
  23.         Thread.sleep(3000);  
  24.         new Thread(new Runnable() {  
  25.   
  26.             public void run() {  
  27.                 asleep = true;  
  28.                 System.out.println("end");  
  29.             }  
  30.         }).start();  
  31.   
  32.     }  
  33.   
  34.     public static void judge() {  
  35.         while (!asleep) {  
  36.             try {  
  37.                 Thread.sleep(500);  
  38.             } catch (InterruptedException e) {  
  39.                 // TODO Auto-generated catch block  
  40.                 e.printStackTrace();  
  41.             }  
  42.             CMS();  
  43.         }  
  44.     }  
  45.   
  46.     public static void CMS() {  
  47.         System.out.println("@");  
  48.     }  
  49. }  

----------------------------

那么,我们这里说线程要先拷贝变量到自己的工作内存,然后再使用。在这里,什么是线程的工作内存呢?

看看JLS(java语言规范)对线程工作内存的描述,线程的working memory只是cpu的寄存器和高速缓存的抽象描述。

`volatile` 是 Java 中的一个关键字,主要用于线程之间的可见性控制。当我们希望某个变量的状态修改能够立即对其他线程可见时,可以将该变量声明为 `volatile`。 ### 使用场景及作用 1. **保证内存可见性** 当一个线程修改了 `volatile` 变量的值时,新值会立刻被更新到主存中,并且其他线程读取这个变量时也能从主存获取最新的值,而不是缓存在本地 CPU 的副本上。 2. **防止指令重排序** JVM 为了优化性能可能会对程序中的操作进行指令重排,而通过使用 `volatile` 关键字修饰变量,则能禁止对其相关的某些操作进行重排序,从而避免潜在的数据竞争问题。 3. **不具备原子性** 需要注意的是,虽然 volatile 能够提供变量状态改变后的即时通知功能以及一定程度上的顺序保障,但它并不能替代锁机制 (synchronized 或 Lock),因为它无法保证复合动作如 i++ 这样的操作具有原子性。 4. **典型应用场景** - 状态标志位:用于指示某种条件是否成立。 - 单例模式延迟加载实例化等需要考虑多线程环境下共享数据一致性的场合。 ```java public class VolatileExample { private static volatile boolean flag = true; public static void main(String[] args) throws InterruptedException { new Thread(() -> { while(flag){ // Do something... } System.out.println("Thread stopped."); }).start(); Thread.sleep(500); // Simulate delay for starting another thread. new Thread(() -> { try{ Thread.sleep(200); } catch(Exception e){} flag=false; }).start(); } } ``` 在这个例子中,“flag”被设定了volatile属性后,在第二个线程将其设置成false之后,第一个线程就可以感知到这种变化并终止循环运行。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值