Java同步并发生在之前

时间:2015-02-07 16:37:53

标签: java multithreading synchronized happens-before

synchronized语句建立之前发生的关系。但我不确定细节。 在http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/package-summary.html,可以阅读

  

监视器的解锁(同步块或方法退出)发生在每次后续锁定之前   同一监视器的(同步块或方法条目)

我想知道我是否理解正确。因此,请看下面的例子。 让我们假设有2个线程T1,T2共享类Data的相同实例数据和Object类的对象。 现在,以下代码在给定的线程和顺序中执行:

(1)T1:  data.setValue("newValue");
(2)T1:  synchronized(object){}

(3)T2:  synchronized(object){}
(4)T2:  String str=data.getValue();

因为(1)和(2)在同一个线程中执行,一个有hb(1,2)和模拟hb(3,4)。在(2)中是监视器的解锁和(3)同一监视器的锁定,因此hb(2,3),因此hb(1,4)和str应该等于" newValue&#34 ;。那是对的吗?如果不是hb(2,3)应该是错的,为什么呢?

修改

因为需要详细的类数据来回答这个问题:

public class Data {

private String value

 public void setValue(String newValue){
     value=newValue;
 }

 public String getValue getValue(){
     return value;
 }
}

编辑2 它清楚,一个不能保证执行的顺序。当有人改为

(1*)T1:  synchronized(object){data.setValue("newValue");}

(2*)T2:  synchronized(object){String str=data.getValue();}

一个人也不能保证(1 *)在(2 *)之前被排除,但是如果我是正确的,则可以保证在(2 *)之后有一个str =" newValue" if(1 *)之前执行(2 *)。我想知道第一个例子是否同样适用

3 个答案:

答案 0 :(得分:1)

  

因为(1)和(2)在同一个线程中执行,一个有hb(1,2)和模拟hb(3,4)。在(2)中是监视器的解锁和(3)同一监视器的锁定,因此hb(2,3),因此hb(1,4)和str应该等于“newValue”。这是对的吗?

是的,您的逻辑对于此特定方案是正确的。如果{且仅当} 23之前执行,则hb(2, 3)。要理解它应该是什么,想象一下如下的线程进程:

localState *= 2;
synchronized(object) {
    sharedState = localState;
}

虽然localState计算之外的同步块,但是其他线程必须看到此计算看到{{的正确值1}}。

然而,重要的是要理解没有理由期待您所询问的订单作为结果。例如,执行这种方式很容易:

(1)T1:  data.setValue("newValue");

(3)T2:  synchronized(object){}
(4)T2:  String str=data.getValue();

(2)T1:  synchronized(object){}

这很糟糕,因为现在sharedState正在写入内存中的某个位置而没有同步,而T1即将读取它。 (T2甚至可以在写入的同时读取!)


要了解发生了什么 - 之前就是这样,相反,想象这些线程并发运行(如线程那样)并在以下时间轴下执行:

  |            T1              |              T2
-------------------------------------------------------------
1 | synchronized(object){}     |
2 | data.setValue("newValue"); | String str=data.getValue();
3 |                            | synchronized(object){}

请注意我如何对齐这些假设行为。

  • T2点,1获取锁定并将其释放。
  • T1点,2同时执行写入T1执行读取。
  • T2点,3获取锁定并将其释放。

T2实际上首先发生2写或T1读了?

同步不保证线程实际相对于彼此执行的顺序。相反,它是关于线程之间的内存一致性

T2点,因为没有同步,即使2实际上在 T1读取之前实际写了T2也是可以自由查看内存中的值。因此,T2之前T2(2)可能出现

从技术上讲,这意味着在同步之外,线程可以在CPU缓存而不是主内存中自由读/写。同步强制主存储器中的读/写。

现在有了第二个并发时间轴:

             T1              |              T2
------------------------------------------------------------
 synchronized(object){       | synchronized(object){
  data.setValue("newValue"); |  String str=data.getValue();
 }                           | }

虽然我们无法保证哪个线程首先获得锁定,但我们确保内存访问保持一致。我们也保证他们的行动不会重叠,这在第一个时间表中是可能的。

  • 如果T1(2)首先获得锁定,则可以保证T1的同步操作看起来好像在T1的操作之前发生。 (T2肯定会在T1读之前写。)
  • 如果T2首先获得锁定,则可以保证T2的同步操作看起来好像在T2的操作之前发生。 (T1肯定会在T1读后写。)

答案 1 :(得分:0)

没有。在语句3之前,语句2将始终执行或发生并不是必需的。线程2可能会在对象上获取监视器,因此语句3将在语句2之前发生。

您无法控制哪个线程实际上会获取对象的监视器而您无法预测。

答案 2 :(得分:0)

不是那么简单。它还取决于data.setValuedata.getValue实际做了什么。这些方法对于并发(非同步)调用是否安全? 在一个人为的例子中,如果数据由HashMap支持,并且多个线程同时调用各种set方法,则可以lead to an infinite loop

简而言之,您只能保证执行的顺序。你对set和get之间的内存可见性有一些有限的保证,但是没有并发调用来设置或得到任何潜在的副作用。