进入同步块是原子的吗?
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" 值用于锁定。但它不会阻止两个线程同时修改同一个实例:
- 线程 M 获取旧值的锁。线程 1 读取旧值。
- 线程 M 更改值。
- 线程 M 释放对旧值的锁定。线程 2 读取新值。
- 线程 1 获取旧值的锁。线程 2 获取新值的锁。
- 线程 1 读取新值。线程 2 读取新值。
- 线程 1 修改新值。线程 2 修改新值。
要避免这一切,只需创建一个单独的对象进行锁定,永远不要更改它。
假设你有一些变量,foo
:
Foo foo;
假设它持有一个对象的引用:
foo = new Foo(...);
假设我们有一个 synchronized
块:
synchronized(foo) {
...
}
synchronized
关键字不对变量foo
进行操作,并且不进行操作关于同步块中的语句。
synchronized
关键字在这里做的唯一一件事是防止其他线程同时在同一 实例 上同步。
如果重新分配变量 foo
以在线程 A 位于块内时引用某个不同的实例,则其他线程 B 将能够同时进入同一块,因为每个这两个线程将在不同的实例上同步。
你知道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" 值用于锁定。但它不会阻止两个线程同时修改同一个实例:
- 线程 M 获取旧值的锁。线程 1 读取旧值。
- 线程 M 更改值。
- 线程 M 释放对旧值的锁定。线程 2 读取新值。
- 线程 1 获取旧值的锁。线程 2 获取新值的锁。
- 线程 1 读取新值。线程 2 读取新值。
- 线程 1 修改新值。线程 2 修改新值。
要避免这一切,只需创建一个单独的对象进行锁定,永远不要更改它。
假设你有一些变量,foo
:
Foo foo;
假设它持有一个对象的引用:
foo = new Foo(...);
假设我们有一个 synchronized
块:
synchronized(foo) {
...
}
synchronized
关键字不对变量foo
进行操作,并且不进行操作关于同步块中的语句。
synchronized
关键字在这里做的唯一一件事是防止其他线程同时在同一 实例 上同步。
如果重新分配变量 foo
以在线程 A 位于块内时引用某个不同的实例,则其他线程 B 将能够同时进入同一块,因为每个这两个线程将在不同的实例上同步。