是一个易变的 int 在java线程安全中?也就是说,它是否可以在不锁定的情况下安全地读写?
int
0lvr5msh1#
[…]是指在没有锁定的情况下可以安全地读写吗?是的,读操作总是会得到最后一次写操作的值(读操作和写操作都是原子操作)。volatile读/写在执行过程中引入了所谓的happens-before关系。摘自java语言规范第17章:线程和锁对易失性字段的写入(§8.3.1.4)在该字段的每次后续读取之前发生。换句话说,在处理易失性变量时,不必使用 synchronized 关键字,以确保线程获取写入变量的最新值。不过,正如jonskeet指出的,volatile变量的使用是有限的,一般来说,您应该考虑使用 java.util.concurrent 改为打包。
synchronized
java.util.concurrent
tkclm6bt2#
对java中volatile int的访问将是线程安全的。当我说access时,我指的是它上面的单元操作,比如volatile\u var=10或int temp=volatile\u var(基本上用常量值写/读)。java中的volatile关键字确保了两件事:当你读的时候,你总是在主存里得到值。通常为了优化目的,jvm使用寄存器或更一般的术语本地内存来存储/访问变量。因此在多线程环境中,每个线程可能会看到变量的不同副本。但使其易失性可以确保对变量的写入被刷新到主内存,并且从主内存读取也会发生,从而确保线程可以看到变量的正确副本。对volatile的访问是自动同步的。因此jvm在读/写变量时确保了顺序。然而jonskeet正确地提到,在非原子操作(volatile_var=volatile+1)中,不同的线程可能会得到意外的结果。
l7wslrjt3#
不总是这样。如果多个线程正在写入和读取变量,则这不是线程安全的。如果您有一个writer线程和多个reader线程,那么它是线程安全的。如果您正在寻找安全的线程,请使用atomic类一个支持单变量无锁线程安全编程的小工具箱。本质上,这个包中的类将volatile值、字段和数组元素的概念扩展到那些还提供原子条件更新操作的类:
boolean compareAndSet(expectedValue, updateValue);
请参阅以下帖子中的@teto-answer:volatile boolean与atomicboolean
fnx2tebb4#
如果volatile不依赖于任何其他volatile变量,那么它的线程对读取操作是安全的。在写易失性的情况下,不保证线程安全。假设你有一个易变的变量i,它的值依赖于另一个易变的变量j。现在thread-1访问变量j并将其递增,并将从cpu缓存中更新主内存中的变量。如果线程2读取thread-1之前的变量i实际上可以更新主内存中的j。i的值将与j的旧值相同,这是不正确的。它也被称为脏读。
trnvg8h35#
是的,您可以安全地从它读取和写入它—但是您不能做任何复合操作,例如安全地递增它,因为这是一个读/修改/写入周期。还有一个问题是它如何与其他变量的访问交互。volatile的确切本质是令人困惑的(更多细节请参见jls的内存模型部分)-我个人通常会使用 AtomicInteger 相反,作为一种更简单的方法来确保我做对了。
AtomicInteger
beq87vna6#
1) 如果两个线程都在读写一个共享变量,那么仅使用volatile关键字是不够的。在这种情况下,您需要使用synchronized来保证变量的读写是原子的。读取或写入volatile变量不会阻止线程读取或写入。要做到这一点,您必须在关键部分周围使用synchronized关键字。2) 作为同步块的替代方法,您还可以使用java.util.concurrent包中的许多原子数据类型之一。例如,atomiclong或atomicreference或其中一个。如果您有一个writer线程和多个reader线程,那么它是线程安全的。
class Foo { private volatile Helper helper = null; public Helper getHelper() { if (helper == null) { synchronized(this) { if (helper == null) helper = new Helper(); } } return helper; } }
注意:如果helper是不可变的,那么就不需要volatile关键字。如果计数器被多个线程递增(读写操作),则不会给出正确答案。这个条件也可以用竞态条件来说明。
public class Counter{ private volatile int i; public int increment(){ i++; } }
注意:这里volatile没有帮助。
6条答案
按热度按时间0lvr5msh1#
[…]是指在没有锁定的情况下可以安全地读写吗?
是的,读操作总是会得到最后一次写操作的值(读操作和写操作都是原子操作)。
volatile读/写在执行过程中引入了所谓的happens-before关系。
摘自java语言规范第17章:线程和锁
对易失性字段的写入(§8.3.1.4)在该字段的每次后续读取之前发生。
换句话说,在处理易失性变量时,不必使用
synchronized
关键字,以确保线程获取写入变量的最新值。不过,正如jonskeet指出的,volatile变量的使用是有限的,一般来说,您应该考虑使用
java.util.concurrent
改为打包。tkclm6bt2#
对java中volatile int的访问将是线程安全的。当我说access时,我指的是它上面的单元操作,比如volatile\u var=10或int temp=volatile\u var(基本上用常量值写/读)。java中的volatile关键字确保了两件事:
当你读的时候,你总是在主存里得到值。通常为了优化目的,jvm使用寄存器或更一般的术语本地内存来存储/访问变量。因此在多线程环境中,每个线程可能会看到变量的不同副本。但使其易失性可以确保对变量的写入被刷新到主内存,并且从主内存读取也会发生,从而确保线程可以看到变量的正确副本。
对volatile的访问是自动同步的。因此jvm在读/写变量时确保了顺序。
然而jonskeet正确地提到,在非原子操作(volatile_var=volatile+1)中,不同的线程可能会得到意外的结果。
l7wslrjt3#
不总是这样。
如果多个线程正在写入和读取变量,则这不是线程安全的。如果您有一个writer线程和多个reader线程,那么它是线程安全的。
如果您正在寻找安全的线程,请使用atomic类
一个支持单变量无锁线程安全编程的小工具箱。
本质上,这个包中的类将volatile值、字段和数组元素的概念扩展到那些还提供原子条件更新操作的类:
请参阅以下帖子中的@teto-answer:
volatile boolean与atomicboolean
fnx2tebb4#
如果volatile不依赖于任何其他volatile变量,那么它的线程对读取操作是安全的。在写易失性的情况下,不保证线程安全。
假设你有一个易变的变量i,它的值依赖于另一个易变的变量j。现在thread-1访问变量j并将其递增,并将从cpu缓存中更新主内存中的变量。如果线程2读取
thread-1之前的变量i实际上可以更新主内存中的j。i的值将与j的旧值相同,这是不正确的。它也被称为脏读。
trnvg8h35#
是的,您可以安全地从它读取和写入它—但是您不能做任何复合操作,例如安全地递增它,因为这是一个读/修改/写入周期。还有一个问题是它如何与其他变量的访问交互。
volatile的确切本质是令人困惑的(更多细节请参见jls的内存模型部分)-我个人通常会使用
AtomicInteger
相反,作为一种更简单的方法来确保我做对了。beq87vna6#
1) 如果两个线程都在读写一个共享变量,那么仅使用volatile关键字是不够的。在这种情况下,您需要使用synchronized来保证变量的读写是原子的。读取或写入volatile变量不会阻止线程读取或写入。要做到这一点,您必须在关键部分周围使用synchronized关键字。
2) 作为同步块的替代方法,您还可以使用java.util.concurrent包中的许多原子数据类型之一。例如,atomiclong或atomicreference或其中一个。
如果您有一个writer线程和多个reader线程,那么它是线程安全的。
注意:如果helper是不可变的,那么就不需要volatile关键字。
如果计数器被多个线程递增(读写操作),则不会给出正确答案。这个条件也可以用竞态条件来说明。
注意:这里volatile没有帮助。