Java getter和setter应该同步吗?
在上面的示例中,让getter同步有什么意义吗?我认为最好在这里引用: 假设只有在写入共享变量时才需要使用同步,这是一个常见的错误;这根本不是事实 对于可由多个用户访问的每个可变状态变量 线程,对该变量的所有访问必须使用相同的 锁上了。在这种情况下,我们说变量由 锁Java getter和setter应该同步吗?,java,synchronized,Java,Synchronized,在上面的示例中,让getter同步有什么意义吗?我认为最好在这里引用: 假设只有在写入共享变量时才需要使用同步,这是一个常见的错误;这根本不是事实 对于可由多个用户访问的每个可变状态变量 线程,对该变量的所有访问必须使用相同的 锁上了。在这种情况下,我们说变量由 锁 在没有同步的情况下,编译器、处理器和运行时可以对操作的执行顺序做一些完全奇怪的事情。试图推断在同步不足的多线程程序中内存操作“必须”发生的顺序几乎肯定是错误的 通常,您不必对原语如此小心,因此如果这是int或boolean,则可能是
在没有同步的情况下,编译器、处理器和运行时可以对操作的执行顺序做一些完全奇怪的事情。试图推断在同步不足的多线程程序中内存操作“必须”发生的顺序几乎肯定是错误的 通常,您不必对原语如此小心,因此如果这是
int
或boolean
,则可能是:
当线程在没有同步的情况下读取变量时,它可能会看到
过时的值,但至少它可以看到实际放置的值
这里有一些线程,而不是一些随机值
但是,对于64位操作,例如在long
或double
上,如果未声明为volatile
,则情况并非如此:
Java内存模型需要fetch和
存储操作是原子的,但对于非易失性的long和double
变量,JVM可以将64位读或写处理为两个
单独的32位操作。如果读取和写入以不同的方式进行
线程,因此可以读取非易失性long并获取
返回一个值的高32位和另一个值的低32位
因此,即使您不关心过时的值,使用它也是不安全的
多线程程序中共享可变长变量和双变量
除非它们被宣布为易变或由锁保护
让我通过示例向您展示JIT编译代码的合法方式。你写道:
private double value;
public synchronized void setValue(double value) {
this.value = value;
}
public double getValue() {
return this.value;
}
JIT编译:
while (myBean.getValue() > 1.0) {
// perform some action
Thread.sleep(1);
}
在稍微不同的场景中,甚至Java编译器也可以生成类似的字节码(它只需要消除动态分派到不同的getValue
)的可能性。这是一个吊装的教科书示例
为什么这是合法的?编译器有权假设执行上述代码时,
myBean.getValue()
的结果永远不会改变。如果没有同步,则允许忽略其他线程的任何操作。这里的原因是防止任何其他线程在线程读取时更新值,从而避免对过时的值执行任何操作
这里get方法将获取“this”上的内在锁,因此可能尝试使用setter方法设置/更新的任何其他线程都必须等待获取“this”上的锁,才能进入执行get的线程已经获取的setter方法
这就是为什么在可变状态下执行任何操作时,建议遵循使用相同锁的做法
由于没有复合语句,因此在这里使字段不稳定将起作用
需要注意的是,同步方法使用内在锁,即“this”。因此,同步get和set意味着任何进入该方法的线程都必须获得该方法的锁
在执行非原子64位操作时,应特别考虑。从Java并发实践中摘录的内容有助于理解这种情况- “Java内存模型要求获取和存储操作是原子的,但对于非易失性长变量和双变量,JVM允许将64位读或写作为两个独立的32位 位操作。如果读取和写入发生在不同的线程中,则可以读取非易失性long并返回一个值的高32位和另一个值的低32位。因此,即使您不关心过时的值,它也会 在多线程程序中使用共享可变长变量和双变量是不安全的,除非声明它们
易变的或由锁保护的。”也许对于某些人来说,这段代码看起来很糟糕,但它工作得很好
if (myBean.getValue() > 1.0)
while (true) {
// perform some action
Thread.sleep(1);
}
更详细地说,如果希望在多线程应用程序中获得最新版本的值,则应该同步getter和setter。如果,出于某些非常奇怪的原因,您不关心获取最新版本,并且同步占用了太多时间(这种情况不太可能!),我认为您可以不同步getter。除非在这种情况下,-double是64位的,所以它应该始终被同步。@user949300不同步getter是不正确的。您可能永远都不会看到var的更改。@user949300不仅无法获得最新版本,而且您甚至不知道将获得哪个版本,而且该版本可能因线程而异。这几乎是不可接受的场景……”“在没有同步的情况下,编译器、处理器和运行时可以对操作的执行顺序做一些完全奇怪的事情”——参见示例17.4.5-1(在一致性之前发生)在@gasan中,JVM是32位还是64位无关紧要,因为我们在规范级别讨论这个问题。JLS将
long
和double
操作指定为非原子操作。但是,所有这些都与新的内存模型定义无关,在新的内存模型定义中,很明显,如果没有适当的,就没有交易发生在排序之前。如果您使用的是Java 1.5或更大版本,并且只使用set和get(setValue
则不必同步),则使双字段不稳定将是令人满意的。参见java.util.concurrent.atomic.atomic*类和已经引用的java并发性实践。@s106mo这在al上有效
private Double value;
public void setValue(Double value){
updateValue(value, true);
}
public Double getValue(){
return updateValue(value, false);
}
private double updateValue(Double value,boolean set){
synchronized(MyClass.class){
if(set)
this.value = value;
return value;
}
}