private double value;
public synchronized void setValue(double value) {
this.value = value;
}
public double getValue() {
return this.value;
}
在上面的例子中,使getter同步有意义吗?private double value;
public synchronized void setValue(double value) {
this.value = value;
}
public double getValue() {
return this.value;
}
在上面的例子中,使getter同步有意义吗?long
或double
(如果它们没有声明为volatile
),情况并非如此。Java内存模型要求获取和存储操作是原子的,但对于非易失性的长整型和双精度浮点型变量,JVM允许将64位读取或写入视为两个独立的32位操作。如果读取和写入发生在不同的线程中,则有可能读取非易失性长整型并返回一个值的高32位和另一个值的低32位。因此,即使您不关心陈旧的值,除非这些变量被声明为易失性或受锁保护,否则在多线程程序中使用共享可变的长整型和双精度浮点型变量是不安全的。long
和double
操作定义为非原子操作。但是,所有这些在新的内存模型定义中都不那么重要,因为清楚地指出没有适当的“happens-before”顺序就没有交易。 - Marko Topolnik让我通过示例向您展示JIT编译您的代码的合法方式。您编写:
while (myBean.getValue() > 1.0) {
// perform some action
Thread.sleep(1);
}
JIT编译:
if (myBean.getValue() > 1.0)
while (true) {
// perform some action
Thread.sleep(1);
}
在稍微不同的情况下,甚至Java编译器也可能会生成类似的字节码(只需消除动态分派到不同getValue
的可能性)。这是提升的典型例子。
为什么这样合法?编译器有权假定在执行上述代码时myBean.getValue()
的结果永远不会改变。没有synchronized
,它可以忽略其他线程的任何操作。
synchronized
的情况下这种排列是可能的,而synchronized
则会阻止它们发生? - dhblahmyBean
,JIT 编译器会知道这个情况。如果它确定“一些操作”可能会更改 myBean
,那么就不会进行优化。至于其他线程,这是基本的 Java 内存模型语义:如果没有同步/易失性,运行时在推理代码时可以忽略其他线程的操作。 - Marko Topolnik 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;
}
}