java变量本身是线程安全的吗?更新变量时?
假设我有两个线程更新一个对象,一个线程不同步地从该对象读取.显然,这是运行条件.但是,我想知道变量本身是否只能部分写入.
Suppose I have two threads updating an object, and one thread reading from that object with no synchronization. Obviously, this is run condition. However, I am wondering if the variable itself can only partially written.
public class CommonObject extends Object
{
static int memberVar=-1;
}
public class Input1Thread extends Thread
{
public void run()
{
while(true)
CommonObject.memberVar = 1
}
}
public class Input2Thread extends Thread
{
public void run()
{
while(true)
CommonObject.memberVar = 2;
}
}
public class OutputThread extends Thread
{
public void run()
{
while(true)
System.out.println("CommonObject.memberVar"+ CommonObject.memberVar);
}
}
我会假设打印出来的值也是 2 或 1.但是,我想知道变量是否有可能被设置到一半?
I would assume the value printed out will also be either 2 or 1. However, I was wondering if it was possible that the variable might be halfway set?
我以原语为例,但如果对象不同,我希望答案也适用于对象.
I used primitives as an example, but I would like the answer would for objects also, if it is different.
推荐答案
这取决于变量的类型.
double
s 和 long
s(Java 中的两种 64 位类型)如果不是 volatile
,而所有其他类型(包括引用)可能永远不会撕裂.字撕裂会给您带来您担心的行为:一些字节来自旧值,其中一些来自新值,总体结果是一个既不是旧值也不是新值的值.
double
s and long
s (the two 64-bit types in Java) are allowed to word-tear if they're not volatile
, while all other types (including references) may never tear. Word tearing would give you the behavior you're worried about: some of the bytes are from the old value, some of them are from the new value, and the overall result is a value that's neither the old nor new.
这在 JLS 17.7 中指定:
出于 Java 编程语言内存模型的目的,对非易失性 long 或 double 值的单次写入被视为两次单独的写入:每个 32 位一半.这可能会导致线程从一次写入中看到 64 位值的前 32 位,而从另一次写入中看到后 32 位.
For the purposes of the Java programming language memory model, a single write to a non-volatile long or double value is treated as two separate writes: one to each 32-bit half. This can result in a situation where a thread sees the first 32 bits of a 64-bit value from one write, and the second 32 bits from another write.
volatile long 和 double 值的写入和读取始终是原子的.
Writes and reads of volatile long and double values are always atomic.
对引用的写入和读取始终是原子的,无论它们是作为 32 位还是 64 位值实现的.
Writes to and reads of references are always atomic, regardless of whether they are implemented as 32-bit or 64-bit values.
当然,引入数据竞争会带来很多问题;但是您的问题专门针对单词撕裂,所以我只在这里解决这个问题,除了要注意仅仅因为你可以,并不意味着你应该".您应该仔细分析您拥有的每个数据竞争,并证明它是良性的(因为其中一些是良性的——例如 String.hashCode 对其值的缓存).
Of course, introducing data races introduces a whole host of issues; but your question was specifically targeted at word tearing, so I'm only addressing that here, except to note that "just because you can, doesn't mean you should." You should be careful to analyze each data race you have and prove that it's benign (as some of them are -- like String.hashCode's caching of its values).
相关文章