进入同步块是原子的吗?

Is entering synchronized block atomic?

你知道java中的同步块是否保证是原子的吗?

想象一下下面的情况

Thread_1,2:

synchronized(object){object.modify();}

(对象是共享变量。)

假设thread_M 将更改对对象的引用,如

synchronized(object){object = new Object()}

现在假设线程 1 和 2 正在竞争获取对象的锁

是否有可能发生以下情况:
1.线程1:读取旧对象
2. ThreadM: 修改对象引用并释放旧对象锁
3.线程2:读取新对象;检查锁;锁定它
4. Thread1:检查锁(ok cos old object was read);锁定它
现在两个线程都有锁并修改相同的(新)对象

所以要具体说明我的问题 - 是否可以保证同步(对象)步骤(1 和 4)是原子的(如步骤 3 中所述)?

您正在同步 "object" 指向的对象,而不是保存值的变量。

但是,因为这两段代码在继续之前在您的对象上同步,所以您是安全的——尽管这是一个糟糕的设计模式。

如果您使用同步方法而不是同步代码块,您可能会发现混淆更少。

此外,这只是我的意见,但 synchronized(object) 似乎是一个非常糟糕的设计模式。这只是我的意见,但我从来没有这样做过。

当你在 object 上同步时,你 可以 重新分配 object,但我想不出重新分配用于锁定的字段的场景是个好主意。

在线程 M 退出其同步块之前,其他线程将无法获取 object 的旧值的锁,但另一个线程将能够尽快获取新对象的锁它对该线程可见。

线程在释放锁之前所做的修改保证对之后获取锁的线程可见。但是由于您正在重新分配锁本身,获取线程可能看不到它已被更改,并且获取旧值上的锁。然后他们仍然看不到 object 已被重新分配。

object 声明为 volatile 变量将确保其 "current" 值用于锁定。但它不会阻止两个线程同时修改同一个实例:

  1. 线程 M 获取旧值的锁。线程 1 读取旧值。
  2. 线程 M 更改值。
  3. 线程 M 释放对旧值的锁定。线程 2 读取新值。
  4. 线程 1 获取旧值的锁。线程 2 获取新值的锁。
  5. 线程 1 读取新值。线程 2 读取新值。
  6. 线程 1 修改新值。线程 2 修改新值。

要避免这一切,只需创建一个单独的对象进行锁定,永远不要更改它。

假设你有一些变量,foo:

Foo foo;

假设它持有一个对象的引用:

foo = new Foo(...);

假设我们有一个 synchronized 块:

synchronized(foo) {
    ...
}

synchronized关键字对变量foo进行操作,并且进行操作关于同步块中的语句。

synchronized 关键字在这里做的唯一一件事是防止其他线程同时在同一 实例 上同步。

如果重新分配变量 foo 以在线程 A 位于块内时引用某个不同的实例,则其他线程 B 将能够同时进入同一块,因为每个这两个线程将在不同的实例上同步。