Java内存模型与并发读
有没有可能Java内存模型与并发读,java,multithreading,java-memory-model,Java,Multithreading,Java Memory Model,有没有可能o2==null&&o1!=空值? 为什么? 为了明确我的意思,我编辑了: 如果出现以下情况,该怎么办: C c = new C(); Thread#1 Object o1 = c.get(); // 1 Object o2 = c.get(); // 2 Thread#2 c.set(new Object()); 这是不可能的,尽管事实上你有一个数据竞赛 数据竞争是因为围绕着o的get和set没有同步,这意味着在对它们进行排序之前不会发生任何事情。您可以通过使这两种方法同
o2==null&&o1!=空值
?
为什么?
为了明确我的意思,我编辑了: 如果出现以下情况,该怎么办:
C c = new C();
Thread#1
Object o1 = c.get(); // 1
Object o2 = c.get(); // 2
Thread#2
c.set(new Object());
这是不可能的,尽管事实上你有一个数据竞赛 数据竞争是因为围绕着
o
的get和set没有同步,这意味着在对它们进行排序之前不会发生任何事情。您可以通过使这两种方法同步,或者使o
不稳定,或者通过其他一些方法来解决这个问题
在没有同步的情况下,JVM可以像其他线程一样对事件重新排序。从Thread1的角度来看,您有以下事件(为简单起见,方法内联):
幸运的是,有两个限制使这项工作正常进行:
c.c=null
发生在所有其他操作之前(请参阅)o1=c.o
发生在o2=c.o
之前。(Thread2不必按该顺序查看这些读取……但它根本看不到o1
或o2
,因此没有问题。)c.o=null
操作并在c.c=newobject()之后对其排序。第二个意思是,从Thread1的角度来看,您在文章底部提到的重新排序是不允许的(当然,Thread1是唯一一个看到o1或o2的线程)
结合这两个限制,我们可以看到,如果Thread1曾经看到c.o
为非null,那么它将永远不会看到它再次恢复为null。如果o1不为空,那么o2也必须为空
请注意,这是相当易变的。例如,假设Thread2不只是设置c.o
一次,而是设置两次:
c.o = null; // initial value
o1 = c.o;
o2 = c.o;
c.o = new Object(); // from Thread2
在这种情况下,可以看到o1
为“二”,而o2
为“一”。这是因为那里的业务有:
c.set("one");
c.set("two");
JVM可以按照自己认为合适的方式从Thread2对项目重新排序,只要它们不在Thread2之前c.c=null
。特别是,这是有效的:
c.o = null; // initial value
o1 = c.o;
o2 = c.o;
c.c = "one"; // from Thread2
c.c = "two"; // from Thread2
通过将get和set同步到o
,删除数据争用将解决这一问题。尽管存在数据争用,但这是不可能的
数据竞争是因为围绕着o
的get和set没有同步,这意味着在对它们进行排序之前不会发生任何事情。您可以通过使这两种方法同步,或者使o
不稳定,或者通过其他一些方法来解决这个问题
在没有同步的情况下,JVM可以像其他线程一样对事件重新排序。从Thread1的角度来看,您有以下事件(为简单起见,方法内联):
幸运的是,有两个限制使这项工作正常进行:
c.c=null
发生在所有其他操作之前(请参阅)
从给定线程的角度来看,在该线程上发生的操作总是按照它们在代码中出现的相同顺序发生(也是JLS 17.4.5)。因此,对于Thread1,o1=c.o
发生在o2=c.o
之前。(Thread2不必按该顺序查看这些读取……但它根本看不到o1
或o2
,因此没有问题。)
其中第一个意味着我们不能执行c.o=null
操作并在c.c=newobject()之后对其排序。第二个意思是,从Thread1的角度来看,您在文章底部提到的重新排序是不允许的(当然,Thread1是唯一一个看到o1或o2的线程)
结合这两个限制,我们可以看到,如果Thread1曾经看到c.o
为非null,那么它将永远不会看到它再次恢复为null。如果o1不为空,那么o2也必须为空
请注意,这是相当易变的。例如,假设Thread2不只是设置c.o
一次,而是设置两次:
c.o = null; // initial value
o1 = c.o;
o2 = c.o;
c.o = new Object(); // from Thread2
在这种情况下,可以看到o1
为“二”,而o2
为“一”。这是因为那里的业务有:
c.set("one");
c.set("two");
JVM可以按照自己认为合适的方式从Thread2对项目重新排序,只要它们不在Thread2之前c.c=null
。特别是,这是有效的:
c.o = null; // initial value
o1 = c.o;
o2 = c.o;
c.c = "one"; // from Thread2
c.c = "two"; // from Thread2
通过将get和set同步到o
,删除数据争用,可以解决这个问题。我不明白get()
如何可能会返回除null
之外的任何内容o
永远不会初始化,set()
仅在参数为null
No时修改o
,因为c1是在c2之前检索的,在任何其他线程中都无法将其还原为null。但是,c2可能是非空的,而c1是空的。我不知道get()
如何可能返回除null
之外的任何内容o
永远不会初始化,set()
仅在参数为null
No时修改o
,因为c1是在c2之前检索的,在任何其他线程中都无法将其还原为null。但是,当c1为空时,c2可以为非空null@Gilgamesz有趣的是,您编辑了它,但仍然没有解决这样一个事实,即键入get()
是为了返回对象,但是当您调用它时,您尝试将其分配给一个C。我是否误解了它应该是Object c1=C.get()
?@Gilgamesz我编辑了我的答案来解释为什么它仍然是安全的。“在线程上发生的操作总是按照它们在代码中出现的相同顺序(也就是JLS 17.4.5)在之前正式发生,所以o1=c.o发生在o2=c.o之前。”你错了。请考虑简单程序的输出:int x,y;螺纹#1:x=1;y=1;线程2:不同步打印(y);在您的示例中,线程1将看到对x的写入发生在写入之前