假设我有两个线程在更新一个对象,一个线程在不同步的情况下从该对象读取。显然,这是运行条件。但是,我想知道变量本身是否只能部分写入。
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。但是,我想知道变量是否可能设置为一半?
我以原语为例,但如果对象不同,我希望答案也适用于对象。
这取决于变量的类型。
doubles和longs(Java中的两种64位类型)如果不是volatile,则允许字眼翻译,而所有其他类型(包括引用)可能永远不会撕裂。单词撕裂会给您带来您担心的行为:某些字节来自旧值,某些字节来自新值,并且总体结果是一个既不是旧值也不是新值的值。
double
long
volatile
这在JLS17.7中指定:
出于Java编程语言内存模型的目的,对非易失性long或double值的单次写入被视为两次单独的写入:一次写入每个32位的一半。这可能导致线程从一次写入中看到64位值的前32位,而从另一次写入中看到后32位的情况。 易失的long和double值的写入和读取始终是原子的。 引用的写入和读取始终是原子的,无论它们是实现为32位还是64位值。
出于Java编程语言内存模型的目的,对非易失性long或double值的单次写入被视为两次单独的写入:一次写入每个32位的一半。这可能导致线程从一次写入中看到64位值的前32位,而从另一次写入中看到后32位的情况。
易失的long和double值的写入和读取始终是原子的。
引用的写入和读取始终是原子的,无论它们是实现为32位还是64位值。
当然,引入数据竞争会带来很多问题。但是您的问题专门针对单词撕裂,因此,我只在这里解决这个问题,只是要注意“仅仅因为您可以,并不意味着您应该这样做”。您应该小心分析每个数据竞争,并 证明 它是良性的(就像其中的一些竞争一样,例如String.hashCode对其值的缓存)。