2025-03-26 00:02:56 +08:00

3.9 KiB
Raw Permalink Blame History

#unity/日常积累

volatile是C#中用于控制同步的关键字其意义是针对程序中一些敏感数据不允许多线程同时访问保证数据在任何访问时刻最多有一个线程访问以保证数据的完整性volatile是修饰变量的修饰符。

1、volatile的使用场景

多个线程同时访问一个变量CLR为了效率允许每个线程进行本地缓存这就导致了变量的不一致性。volatile就是为了解决这个问题volatile修饰的变量不允许线程进行本地缓存每个线程的读写都是直接操作在共享内存上这就保证了变量始终具有一致性。

2、volatile 关键字可应用于以下类型的字段

1、引用类型

2、整型如 sbyte、byte、short、ushort、int、uint、char、float 和 bool。

3、具有整数基类型的枚举类型。

4、已知为引用类型的泛型类型参数。

5、不能将局部变量声明为 volatile。

恐怕比较一下volatile和synchronized的不同是最容易解释清楚的。volatile是变 量修饰符而synchronized则作用于一段代码或方法看如下三句get代码

  1. int i1;              int geti1() {return i1;}
  2. volatile int i2;  int geti2() {return i2;}
  3. int i3;              synchronized int geti3() {return i3;}

  geti1()得到存储在当前线程中i1的数值。多个线程有多个i1变量拷贝而且这些i1之间可以互不相同。换句话说另一个线程可能已经改 变了它线程内的i1值而这个值可以和当前线程中的i1值不相同。事实上Java有个思想叫“主”内存区域,这里存放了变量目前的“准确值”。每个线程 可以有它自己的变量拷贝而这个变量拷贝值可以和“主”内存区域里存放的不同。因此实际上存在一种可能“主”内存区域里的i1值是1线程1里的i1值 是2线程2里的i1值是3——这在线程1和线程2都改变了它们各自的i1值而且这个改变还没来得及传递给“主”内存区域或其他线程时就会发生。
  而geti2()得到的是“主”内存区域的i2数值。用volatile修饰后的变量不允许有不同于“主”内存区域的变量拷贝。换句话说一个变量经 volatile修饰后在所有线程中必须是同步的任何线程中改变了它的值所有其他线程立即获取到了相同的值。理所当然的volatile修饰的变量 存取时比一般变量消耗的资源要多一点,因为线程有它自己的变量拷贝更为高效。
  既然volatile关键字已经实现了线程间数据同步又要synchronized干什么呢呵呵它们之间有两点不同。首 先synchronized获得并释放监视器——如果两个线程使用了同一个对象锁监视器能强制保证代码块同时只被一个线程所执行——这是众所周知的事 实。但是synchronized也同步内存事实上synchronized在“主”内存区域同步整个线程的内存。因此执行geti3()方法做 了如下几步:

  1. 线程请求获得监视this对象的对象锁假设未被锁否则线程等待直到锁释放
  2. 线程内存的数据被消除从“主”内存区域中读入Java虚拟机能优化此步。。。[后面的不知道怎么表达,汗]
  3. 代码块被执行
  4. 对于变量的任何改变现在可以安全地写到“主”内存区域中不过geti3()方法不会改变变量值)
  5. 线程释放监视this对象的对象锁
      因此volatile只是在线程内存和“主”内存间同步某个变量的值而synchronized通过锁定和解锁某个监视器同步所有变量的值。显然 synchronized要比volatile消耗更多资源。