在同步方法或块中访问对象的状态是否安全的问题有答案。我的问题是:访问同步块中的对象X是否安全,其中同步块在另一个对象Y上,并且X有多个写入线程?
public class X{
private int value = 0;
/** set method will be invoked by multiple threads**/
public void set(int v){
this.value = v;
}
public int value(){
return value;
}
}
public class Tester{
private final Object Y = new Object();
public void test(X x){
synchronized(Y){
System.out.println(x.value()); // is it guaranteed that x.value will be read from memory and not from the current thread's cache ?
}
}
}
我知道在不需要volatile的情况下访问定义了同步块的对象的状态是安全的,但若在另一个对象上定义了同步的块呢?
IMO您问错了问题。Java内存模型本身并不关心对象和类。它只讨论变量的可见性。
synchronized(o)
块的可见性规则非常简单:无论一个线程在离开synchronized(o)
块之前对任何变量做了什么,都保证在另一个线程随后进入同一实例o
上的synchronized
块之后,对任何其他线程都是可见的。
因此,在您的示例中,如果您有一些X my_x
,并且某个线程A执行以下操作:
synchronized(Y) {
my_x.set(55);
}
然后,当其他线程B随后调用tester.test(my_x)
时,线程B将看到线程A存储的值。
另一方面,如果线程A调用my_x.set(...)
而不在Y
上同步,那么Java不会承诺线程B何时(如果有的话(会看到更改。
注意:您的程序通过使锁定对象Y
成为Tester
类的private
成员,同时使test(X)
函数public
,公开地招致失败。这实际上要求您(或其他程序员(犯调用tester.test(some_random_X)
的错误,其中some_random_X
是由另一个而不是锁定Y
的线程设置的。
不,它不安全。
与问题无关:
示例代码的第一个问题:
public void set(int v){
this.value = v;
}
这是非常糟糕的代码质量,应该这样写:
public final void setValue(final int value) {
this.value = value;
}
除了添加最后的修饰符可以改进字节码优化之外,这对性能没有任何好处——这只是setter的一种设计模式。
示例代码的第二个问题:
public int value(){
return value;
}
吸气剂的正确设计模式是:
public final int getValue() {
return value;
}
示例代码的第三个问题:
private final Object Y = new Object();
字段"Y"不是静态的,因此不是常量,该字段的正确命名约定为:
private final Object y = new Object();
示例代码的第四个问题是在没有空格的括号后面放大括号,我的意思是不做
){
你真的应该做:
) {