0

同步语句建立了先发生关系。但我不确定细节。在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”。那是对的吗?如果不是 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*) 之后,如果 (1*) 在 (2*) 之前执行,则 str= "newValue" *)。我想知道第一个例子是否同样适用

4

3 回答 3

2

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

是的,您的逻辑对于这种特定情况是正确的。如果(且仅当)在then2之前执行。要理解为什么会这样,请想象一个线程进程,如下所示:3hb(2, 3)

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

尽管localState是在同步块之外计算的,但其他线程应该有必要看到这个计算,才能看到sharedState.

但是,重要的是要了解,没有理由期望您所询问的顺序作为结果。例如,它可以很容易地以这种方式执行:

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

(3)T2:同步(对象){}
(4)T2:字符串str=data.getValue();

(2)T1:同步(对象){}

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


要了解之前发生的一切,请想象这些线程同时运行(就像线程一样)并在以下时间线下执行:

  | T1 | T2
-------------------------------------------------- ------------
1 | 同步(对象){} |
2 | data.setValue("newValue"); | 字符串 str=data.getValue();
3 | | 同步(对象){}

请注意我是如何调整这些假设行动的。

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

实际上首先发生在 point 2T1的写还是T2读?

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

此时2,由于没有同步,即使T1实际在读取之前 进行写入,也可以随意查看内存中的值。因此它可以出现之前发生的事情。T2T2T2(2)T1(2)

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

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

             T1 | T2
-------------------------------------------------- ----------
 同步(对象){ | 同步(对象){
  data.setValue("newValue"); | 字符串 str=data.getValue();
 } | }

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

  • 如果T1先获取锁,则保证T1's 同步动作会出现在T2's 动作之前。(T1肯定会在阅读之前先T2写。)
  • 如果T2先获取锁,则保证T2's 同步动作会出现在T1's 动作之前。(T1一定会在读后写T2。)
于 2015-02-07T19:17:12.900 回答
0

不。语句 2 不一定总是在语句 3 之前执行或发生。线程 2 可能会获取对象上的监视器,因此语句 3 将在语句 2 之前发生。

您无法控制哪个线程将实际获得 Object 的监视器,并且您无法预测。

于 2015-02-07T16:45:17.280 回答
0

这不是那么简单。它还取决于幕后实际做了data.setValue什么。data.getValue这些方法对于并发(非同步)调用是否安全?在一个人为的示例中,如果数据由 a 支持,HashMap并且多个线程同时调用各种 set 方法,则可能导致无限循环

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

于 2015-02-07T17:27:52.567 回答