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

发布于 2024-12-10 15:32:24 字数 74 浏览 0 评论 0原文

Java 中的 volatile int 是线程安全的吗?也就是说,可以在不加锁的情况下安全地读取和写入吗?

Is a volatile int in Java thread-safe? That is, can it be safely read from and written to without locking?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

南七夏 2024-12-17 15:32:24

是的,您可以安全地读取它并写入它 - 但您不能执行任何复合操作,例如安全地递增它,因为这是一个读/修改/写周期。还有一个问题是它如何与其他变量的访问交互。

坦率地说,易失性的确切性质令人困惑(请参阅 JLS 的内存模型部分了解更多详细信息) - 我个人通常会使用AtomicInteger 相反,作为确保我做对的更简单的方法。

Yes, you can read from it and write to it safely - but you can't do anything compound such as incrementing it safely, as that's a read/modify/write cycle. There's also the matter of how it interacts with access to other variables.

The precise nature of volatile is frankly confusing (see the memory model section of the JLS for more details) - I would personally generally use AtomicInteger instead, as a simpler way of making sure I get it right.

一身软味 2024-12-17 15:32:24

[...]能够在不锁定的情况下安全地读取和写入?

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

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

来自 Java 语言规范 第 17 章:线程和锁

对易失性字段 (§8.3.1.4) 的写入发生在该字段的每次后续读取之前。

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

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

[...] as in being able to be safely read from and written to without locking?

Yes, a read will always result in the value of the last write, (and both reads and writes are atomic operations).

A volatile read / write introduces a so called happens-before relation in the execution.

From the Java Language Specification Chapter 17: Threads and Locks

A write to a volatile field (§8.3.1.4) happens-before every subsequent read of that field.

In other words, when dealing with volatile variables you don't have to explicitly synchronize (introduce a happens-before relation) using synchronized keyword in order to ensure that the thread gets the latest value written to the variable.

As Jon Skeet points out though, the use of volatile variables are limited, and you should in general consider using classes from the java.util.concurrent package instead.

童话里做英雄 2024-12-17 15:32:24

Java 中对 volatile int 的访问将是线程安全的。当我说访问时,我指的是对其进行单元操作,例如 volatile_var = 10 或 int temp = volatile_var (基本上是使用常量值进行写入/读取)。 java中的Volatile关键字确保了两件事:

  1. 读取时你总是得到主内存中的值。通常出于优化目的,JVM 使用寄存器或更一般的术语“本地内存”来存储/访问变量。因此在多线程环境中每个线程可能会看到不同的变量副本。但是使其成为易失性可确保对变量的写入刷新到主内存并从主内存读取它,从而确保线程看到变量的正确副本。
  2. 对易失性的访问是自动同步的。因此 JVM 确保读/写变量时的顺序。

然而,Jon Skeet 正确地提到,在非原子操作 (volatile_var = volatile + 1) 中,不同的线程可能会得到意外的结果。

Access to volatile int in Java will be thread-safe. When I say access I mean the unit operation over it, like volatile_var = 10 or int temp = volatile_var (basically write/read with constant values). Volatile keyword in java ensures two things :

  1. When reading you always get the value in main memory. Generally for optimization purposes JVM use registers or in more general terms local memory foe storing/access variables. So in multi-threaded environment each thread may see different copy of variable. But making it volatile makes sure that write to variable is flushed to main memory and read to it also happens from main memory and hence making sure that thread see at right copy of variable.
  2. Access to the volatile is automatically synchronized. So JVM ensures an ordering while read/write to the variable.

However Jon Skeet mentions rightly that in non atomic operations (volatile_var = volatile + 1) different threads may get unexpected result.

最美不过初阳 2024-12-17 15:32:24

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

2) 作为同步块的替代方案,您还可以使用 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;
}
}

注意:如果 helper 是不可变的,则不需要 volatile 关键字。这里单例将正常工作。

如果计数器由多个线程递增(读写操作),则不会给出正确的答案。这种情况也可以通过竞争条件来说明。

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

注意:这里 volatility 没有帮助。

1) If two threads are both reading and writing to a shared variable, then using the volatile keyword for that is not enough. You need to use a synchronized in that case to guarantee that the reading and writing of the variable is atomic. Reading or writing a volatile variable does not block threads reading or writing. For this to happen you must use the synchronized keyword around critical sections.

2) As an alternative to a synchronized block you could also use one of the many atomic data types found in the java.util.concurrent package. For instance, the AtomicLong or AtomicReference or one of the others.

It's thread safe if you have one writer thread and multiple reader threads.

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

Note : If helper is immutable then no need of volatile keyword.Here singleton will work properly.

In case of counter which is being incremented by multiple threads (reading writing operation) will not give correct answer. This condition is also illustrated by race condition.

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

NOTE : Here volatile will not help.

薯片软お妹 2024-12-17 15:32:24

如果易失性变量不依赖于任何其他易失性变量,则其读操作的线程安全。如果写了 volatile 并不能保证线程安全。

假设你有一个易失性变量 i,它的值取决于另一个易失性变量 j。现在Thread-1访问变量j并递增它,并且即将从CPU缓存更新到主内存中。如果 Thread-2 读取
Thread-1之前的变量i实际上可以更新主存中的j。 i 的值将根据 j 的旧值,这是不正确的。也称为脏读。

If a volatile is not dependent on any other volatile variable its thread safe for read operation. In case of write volatile does not guarantee thread safety.

Assume you have a variable i which is volatile and its value is dependent on another volatile variable say j. Now Thread-1 access variable j and increment it and is about to update it in main memory from CPU cache. In case the Thread-2 reads the
variable i before Thread-1 can actually update the j in main memory. The value of i will be as per the old value of j which would be incorrect. Its also called Dirty read.

寂寞笑我太脆弱 2024-12-17 15:32:24

并非总是如此。

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

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

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

本质上,这个包中的类将易失性值、字段和数组元素的概念扩展到还提供以下形式的原子条件更新操作:

boolean compareAndSet(expectedValue, updateValue);

请参阅下面帖子中的 @teto 答案:

Volatile 布尔值与 AtomicBoolean

Not always.

It's not thread safe if multiple threads are writing and reading the variable. It's thread safe if you have one writer thread and multiple reader threads.

If you are looking for Thread safely, use AtomicXXX classes

A small toolkit of classes that support lock-free thread-safe programming on single variables.

In essence, the classes in this package extend the notion of volatile values, fields, and array elements to those that also provide an atomic conditional update operation of the form:

boolean compareAndSet(expectedValue, updateValue);

Refer to @teto answer in below post:

Volatile boolean vs AtomicBoolean

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文