java中的易失性int是线程安全的吗?

ugmeyewa  于 2021-06-27  发布在  Java
关注(0)|答案(6)|浏览(460)

是一个易变的 int 在java线程安全中?也就是说,它是否可以在不锁定的情况下安全地读写?

0lvr5msh

0lvr5msh1#

[…]是指在没有锁定的情况下可以安全地读写吗?
是的,读操作总是会得到最后一次写操作的值(读操作和写操作都是原子操作)。
volatile读/写在执行过程中引入了所谓的happens-before关系。
摘自java语言规范第17章:线程和锁
对易失性字段的写入(§8.3.1.4)在该字段的每次后续读取之前发生。
换句话说,在处理易失性变量时,不必使用 synchronized 关键字,以确保线程获取写入变量的最新值。
不过,正如jonskeet指出的,volatile变量的使用是有限的,一般来说,您应该考虑使用 java.util.concurrent 改为打包。

tkclm6bt

tkclm6bt2#

对java中volatile int的访问将是线程安全的。当我说access时,我指的是它上面的单元操作,比如volatile\u var=10或int temp=volatile\u var(基本上用常量值写/读)。java中的volatile关键字确保了两件事:
当你读的时候,你总是在主存里得到值。通常为了优化目的,jvm使用寄存器或更一般的术语本地内存来存储/访问变量。因此在多线程环境中,每个线程可能会看到变量的不同副本。但使其易失性可以确保对变量的写入被刷新到主内存,并且从主内存读取也会发生,从而确保线程可以看到变量的正确副本。
对volatile的访问是自动同步的。因此jvm在读/写变量时确保了顺序。
然而jonskeet正确地提到,在非原子操作(volatile_var=volatile+1)中,不同的线程可能会得到意外的结果。

l7wslrjt

l7wslrjt3#

不总是这样。
如果多个线程正在写入和读取变量,则这不是线程安全的。如果您有一个writer线程和多个reader线程,那么它是线程安全的。
如果您正在寻找安全的线程,请使用atomic类
一个支持单变量无锁线程安全编程的小工具箱。
本质上,这个包中的类将volatile值、字段和数组元素的概念扩展到那些还提供原子条件更新操作的类:

boolean compareAndSet(expectedValue, updateValue);

请参阅以下帖子中的@teto-answer:
volatile boolean与atomicboolean

fnx2tebb

fnx2tebb4#

如果volatile不依赖于任何其他volatile变量,那么它的线程对读取操作是安全的。在写易失性的情况下,不保证线程安全。
假设你有一个易变的变量i,它的值依赖于另一个易变的变量j。现在thread-1访问变量j并将其递增,并将从cpu缓存中更新主内存中的变量。如果线程2读取
thread-1之前的变量i实际上可以更新主内存中的j。i的值将与j的旧值相同,这是不正确的。它也被称为脏读。

trnvg8h3

trnvg8h35#

是的,您可以安全地从它读取和写入它—但是您不能做任何复合操作,例如安全地递增它,因为这是一个读/修改/写入周期。还有一个问题是它如何与其他变量的访问交互。
volatile的确切本质是令人困惑的(更多细节请参见jls的内存模型部分)-我个人通常会使用 AtomicInteger 相反,作为一种更简单的方法来确保我做对了。

beq87vna

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没有帮助。

相关问题