Java中的volatile关键字 - Break易站
Java中的volatile关键字
使用volatile是另一种使类线程安全的方法(如synchronized,atomic wrapper)。线程安全意味着一个方法或类实例可以被多个线程同时使用,没有任何问题。
考虑下面的简单例子。
class SharedObj { //在一个线程中对sharedVar所做的更改 //可能不会立即反映在其他线程中 static int sharedVar = 6; }
假设两个线程在SharedObj上工作。如果两个线程在不同的处理器上运行,则每个线程都可以拥有自己的sharedVariable本地副本。如果一个线程修改了它的值,这个改变可能不会立即反映到主存中的原始内存中。这取决于缓存的写入策略。现在另一个线程不知道导致数据不一致的修改值。
下图显示如果两个线程在不同的处理器上运行,那么sharedVariable的值在不同的线程中可能会有所不同。
请注意,在没有任何同步操作的情况下写入普通变量可能对任何读取线程都不可见(此行为称为顺序一致性)。尽管大多数现代硬件提供了良好的缓存一致性,因此很可能一个缓存中的更改反映在其他缓存中,但依靠硬件来“修复”错误的应用程序并不是一个好习惯。
class SharedObj { //volatile 关键字在这里确保在一个线程中进行的更改是 立即反映在其他线程中 static volatile int sharedVar = 6; }
请注意,volatile不应与静态修饰符混淆。静态变量是在所有对象之间共享的类成员。主内存中只有一个副本。
volatile和synchronized:
在我们继续之前,让我们看看锁和同步的两个重要特性。
- 互斥:这意味着一次只有一个线程或进程可以执行一段代码(临界区)。
- 可见性:这意味着一个线程对共享数据所做的更改对其他线程可见。
Java的同步关键字保证了互斥和可见性。如果我们使修改共享变量值的线程块同步化,则只有一个线程可以进入该块,并且由它所做的更改将反映在主内存中。所有其他尝试同时进入该块的线程都将被阻塞并进入睡眠状态。
在某些情况下,我们可能只需要可见性而不是原子性。在这种情况下使用同步是矫枉过正的,并可能导致可伸缩性问题。这里不稳定来救援。易变变量具有同步的可见性特征,但不具有原子性特征。volatile变量的值永远不会被缓存,所有的写入和读取操作都将在主存中进行。然而,挥发性物质的使用仅限于非常有限的一组情况,因为大多数时间是需要原子数的。例如一个简单的增量语句,如x = x + 1; 或者x ++似乎是一个单独的操作,但它确实是一个复杂的读 - 修改 - 写操作序列,必须以原子方式执行。
Java 关键字