当前位置: 首页 > 面试题库 >

Java已同步且发生在[重复]之前

卢翔宇
2023-03-14
问题内容

同步语句建立事前关联。但是我不确定细节。在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”
)。我想知道第一个例子是否也一样


问题答案:

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

是的,您的逻辑对于此特定方案是正确的。如果(且仅当)23then 之前执行hb(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获取锁并将其释放。

实际上第一个发生 在点2T1是写还是T2读?

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

在点处2,因为没有同步,所以即使T1实际上是 T2读,T2也可以自由查看内存中的 值。因此,它可以 出现
T2(2)发生过T1(2)

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

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

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

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

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


 类似资料:
  • 问题内容: 这个问题已经在这里有了答案 : 在一致之前如何理解发生的事情 (4个答案) 2年前关闭。 我试图理解Java 发生在订单概念之前的事情,并且有些事情似乎非常令人困惑。据我所知,之前发生的只是一系列动作的顺序,并不提供有关实时执行顺序的任何保证。实际上(强调我的): 应该注意的是,两个动作之间存在先发生关系并不一定意味着在实现中它们必须按照该顺序进行。 如果重新排序产生的 结果与合法执行

  • 我遇到了以下关于从生产者同步发送。我知道上下文生产者中的异步机制 在此将来调用get()将阻塞,直到相关请求完成,然后返回记录的元数据或引发发送记录时发生的任何异常。 什么是真正的意思相关联的请求完成,我是相当这不是指完整的请求,但在什么程度上这个短语是指?直到经纪人?直到生产者等使用的缓冲区...? 当ack=all与同步生产者和异步生产者一起使用时,它有什么不同?两个场景都被阻塞以进行确认?

  • 我有一个问题,关于如何通过Java内存模型保证对象是线程安全的。 我读过很多书,说在构造函数中编写同步作用域没有意义,但为什么没有呢?是的,只要构造中的对象不在线程之间共享(不应该共享),除了构造线程之外,没有其他线程可以访问任何同步的(this{…}),因此,无需在构造函数中设置该范围来排除它们。但同步作用域不仅仅是为了排除;它们还用于创建发生在关系之前的事件。JLS。17.4 这是一个示例代码

  • 主要内容:实例下面是一个具有同步功能的多线程示例,这是和上篇文章同样的例子,它依次打印计数器值,每次运行它时,它产生相同的结果。 实例 每次运行此程序时都会产生相同的结果 -

  • 问题内容: 您能给我解释一下这段Java代码吗?我不明白这种语法。 问题答案: 这意味着该代码块意味着不超过一个线程将能够访问该代码块中的代码。 也意味着您可以在当前实例上进行同步(获取当前实例的锁)。 这是我在Kathy Sierra的Java认证书中找到的。 因为同步确实会损害并发性,所以您不想同步任何超出保护数据所需的代码。 因此,如果方法的范围超出了需要,则可以将同步部分的范围减小到小于完

  • 我正在尝试在几个线程(6)之间同步一个int计数器。下面是我的工人班。我正在尝试同步我的私有字段: 在我的主类中,我使用 for 循环和执行器服务创建线程。请参阅以下内容: 我的反测试变量不同步,它打印不同的数字(非顺序)。我做错了什么?