Java中的volatile int是否是线程安全的?

时间:2011-10-18 09:35:15

标签: java multithreading thread-safety volatile

Java中的volatile int是否是线程安全的?也就是说,可以安全地读取和写入而不锁定吗?

6 个答案:

答案 0 :(得分:65)

是的,你可以从中读取并安全地写入它 - 但是你不能做任何复合的事情,比如安全地增加它,因为这是一个读/修改/写周期。还有一个问题是它如何与其他变量的访问权交互。

volatile的确切性质令人困惑(参见memory model section of the JLS for more details) - 我个人通常会使用AtomicInteger代替,作为一种更简单的方法来确保我得到它右。

答案 1 :(得分:6)

  

[...],因为能够在没有锁定的情况下安全地读取和写入?

是的,读取将始终导致最后一次写入的值(并且读取和写入都是原子操作)。

易失性读/写在执行中引入了所谓的发生前关系。

来自Java语言规范Chapter 17: Threads and Locks

  

写入易失性字段(第8.3.1.4节) - 在每次后续读取该字段之前发生。

换句话说,在处理volatile变量时,您不必使用synchronized关键字显式同步(引入先发生关系),以确保线程获取写入变量的最新值

正如Jon Skeet指出的那样,volatile变量的使用是有限的,你通常应该考虑使用java.util.concurrent包中的类。

答案 2 :(得分:2)

在Java中访问volatile int将是线程安全的。当我说访问时我的意思是它上面的单元操作,比如volatile_var = 10或int temp = volatile_var(基本上是用常量值写/读)。 java中的Volatile关键字确保了两件事:

  1. 阅读时,您始终会获得主内存中的值。通常出于优化目的,JVM使用寄存器或更通用的术语本地存储器存储/访问变量。因此在多线程环境中,每个线程可能会看到不同的变量副本。但是使它变为volatile会确保写入变量被刷新到主内存并且读取它也发生在主内存中,因此确保线程看到变量的正确副本。
  2. 自动同步对volatile的访问。因此JVM确保在读取/写入变量时进行排序。
  3. 然而Jon Skeet正确地提到在非原子操作(volatile_var = volatile + 1)中,不同的线程可能会得到意想不到的结果。

答案 3 :(得分:0)

并非总是如此。

如果多个线程正在写入并读取变量,那么它不是线程安全的。如果您有一个编写器线程和多个读取器线程,那么它的线程是安全的。

如果您正在安全地寻找线程,请使用AtomicXXX

  

一个小型工具包,支持对单个变量进行无锁线程安全编程。

     

本质上,此包中的类将volatile变量,字段和数组元素的概念扩展为也提供表单的原子条件更新操作的那些:

boolean compareAndSet(expectedValue, updateValue);

请参阅以下帖子中的@teto回答:

Volatile boolean vs AtomicBoolean

答案 4 :(得分:0)

如果volatile不依赖于任何其他volatile变量,则其线程可以安全地进行读操作。如果写volatile,则不保证线程安全。

假设你有一个变量i,它是volatile,它的值依赖于另一个volatile变量,例如j。现在,Thread-1访问变量j并将其递增,并将在主存储器中从CPU缓存中更新它。如果Thread-2读取了 在Thread-1之前的变量i实际上可以更新主存储器中的j。 i的值将是j的旧值,这将是不正确的。它也被称为脏读。

答案 5 :(得分:0)

1)如果两个线程都在读取和写入共享变量,那么使用volatile关键字是不够的。在这种情况下,您需要使用synchronized来保证变量的读取和写入是原子的。读取或写入volatile变量不会阻止线程读取或写入。为此,您必须在关键部分周围使用synchronized关键字。

2)作为synchronized块的替代方法,您还可以使用java.util.concurrent包中的众多原子数据类型之一。例如,AtomicLong或AtomicReference或其中一个。

如果您有一个编写器线程和多个读取器线程,那么它的线程安全。

class Foo {
private volatile Helper helper = null;
public Helper getHelper() {
if (helper == null) {
synchronized(this) {
if (helper == null)
helper = new Helper();
}
}
return helper;
}
}

注意:如果帮助程序是不可变的,则不需要volatile关键字。这些单例将正常工作。

如果计数器被多个线程递增(读取写入操作)将无法给出正确答案。种族条件也说明了这种情况。

public class Counter{
private volatile int i;
public int increment(){
i++;
}
}

注意:此处volatile不会有帮助。