Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/341.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java,仅对同步块中的变量或所有实例变量进行主存同步?_Java_Synchronization_Shared Memory - Fatal编程技术网

Java,仅对同步块中的变量或所有实例变量进行主存同步?

Java,仅对同步块中的变量或所有实例变量进行主存同步?,java,synchronization,shared-memory,Java,Synchronization,Shared Memory,考虑以下玩具示例: public class Test { private int nr; private A a; public synchronized A getA() { return a; } public synchronized void setA(A a) { this.a = a; } public static class A{ B b; }

考虑以下玩具示例:

public class Test {

    private int nr;
    private A a;


    public synchronized A getA() {
        return a;
    }

    public synchronized void setA(A a) {
        this.a = a;
    }


    public static class A{
        B b;
    }

    public static class B{
        // may contain other references
    }
}
由于get/set中的synchronize关键字,我们可以安全地从多个线程读取/写入“a”。我们不需要将其声明为volatile,因为在同步块的开始/结束时,本地缓存线程与主内存的同步已经完成

但是,基本上我们是在“this”上同步的,测试对象也包含变量“nr”。关于set/get'a,Java与主存的同步是否只针对'a'发生,因为它是在这些同步块中访问的唯一变量,还是针对测试类的所有成员,因为我们已经对'this'进行了同步

另外,当与主存发生同步时,这是递归的,即“a”可能包含的所有引用链(在本例中为“b”)都是从主存读/写的,还是仅仅是“a”的引用


谢谢

同步的所做的一切都是为了在
这个
对象上获得一个锁。如果一个线程调用同步方法,那么另一个线程尝试调用同一对象上的同步方法,则第二个调用将被阻止,直到第一个调用完成并释放锁
synchronized
对对象字段的访问方式没有任何限制。

在方法中访问的任何字段都是无关的。。。只能同时执行一个同步方法。如果要锁定不同的字段,则需要手动同步

对于第二个问题,这种同步方法是“可重入的”,这意味着您可以递归地调用相同的方法或任何其他同步方法,而不必担心死锁。

来自

只有在写操作发生在读操作之前,一个线程的写操作的结果才能保证对另一个线程的读操作可见

监视器的解锁(同步块或方法退出)发生在同一监视器的每个后续锁定(同步块或方法进入)之前。而且因为“发生在之前”关系是可传递的,所以在解锁之前线程的所有操作都发生在监视的任何线程锁定之后的所有操作之前

所以你可以说所有的变量都是同步的。更准确的说法是,如果两个线程在同一个对象上同步,那么所有写入(到任何变量)都会被读取(来自相同变量)看到

如果您有多个线程A、B和C,例如,线程A和B在test1上同步,线程B和C在test2上同步。然后,B将看到A的写操作。线程C将看到B的写操作,可能会也可能不会看到A的写操作

例如,对于两个线程T1和T2,语句从上到下执行

T1             T2
test.nr = 5;   
test.setA(a1);
               test.getA(); //gives a1
               print(test.nr); //gives 5
使用不同的对象:

T1             T2
test2.nr = 5;   
test.setA(a1);
               test.getA(); //gives a1
               print(test2.nr); //gives 5
前面的示例未正确同步:

T1             T2
               test.nr = 6;
test.nr = 5;   
test.setA(a1);
               test.getA(); //gives a1
               print(test.nr); //gives 5 or 6
再举一个例子

T1             T2             T3
test.nr = 5;   
test.setA(a1);
               test.nr = 6;   
               test.setA(a2); 
                              test.nr = 7;
                              test.getA(); //gives a2
                              print(test.nr); //gives 5,6 or 7
“主存”里有什么?5、6还是7?嗯,嗯,说明书中没有主存储器。所以没有正确的答案。当然,JVM将使用主内存、寄存器和缓存。但是它如何使用它们取决于JVM的实现

最后一个可怕的例子

T1             T2

               test.nr = 6;
               print(test.nr); //gives 5 or 6
test.nr = 5;   
您可以读取值5,即使它尚未写入


有一个简单的解决方案,如果两个线程读取和/或写入同一个变量,将它们放在同一对象的同步块中。

理论上,只有a是同步的,尽管在实践中,nr不太可能也同步。@assylias我认为该语句不正确。如果两个线程在同一个对象上同步,那么对nr的所有写入都保证被看到。类似的问题:
X
对应于
nr
,而
Y
对应于
这一点。a
@Ishtar否-从挂钟的角度想象线程T1和T2以及以下按该顺序执行的情况(假设nr不是私有的,否则它根本不可访问):
T1:test.setA(a);test.nr=1;T2:test.nr=123;T1:int i=test.nr;
那么T1可以读取1或123。在
nr=123
i=nr
之间没有before关系,所以即使从挂钟的角度来看,它们是按顺序发生的,T1也可能会漏掉新值。@assylas你是对的,我的评论不准确。(只有写入在同步之前进行,读取在同步之后进行,并且没有中间写入时才是正确的)。如果OP的意思是
T1:test.nr=1;test.setA(a);T2:test.getA();int i=test.nr;
并且肯定会看到对nr的其他写入,
i=1;
我指的是,而且:"如果我们从不同的线程读取和写入同步块内的变量,我们总是希望线程1看到线程2设置的值;仅仅在寄存器中看到本地缓存的副本是不正确的。因此,在进入和退出同步块时,必须对主内存进行相关的读取/写入,并且必须在您链接中的规范是在编写自己的Java虚拟机时帮助指导用户:确保同步方法按照规范运行。您肯定不是在编写JVM(您的问题是Java,而不是汇编、C/C++等)。该规范是针对“JVM作者”的“。对于“Java程序员”透视图(您),您好,我有点困惑。我的意思是我知道synchronize(对可能从不同线程运行的代码的顺序访问)的用法,但这种与主存的同步让我感到“模糊”。假设我们输入getA。我假设“a”将从主内存中读取。但是,由于我们在“this”上进行了同步,因此也将从内存中读取“nr”(因为它是一个实例变量,属于我们用来同步的“this”)@user3030447 JLS中没有主内存。因此,无法谈论abo