重新检查 "synchronized" 锁的可变引用?
Re-Check mutable reference for "synchronized" lock?
我想知道是否有一种简单的方法可以创建一个 synchronized
锁来响应不断变化的引用。我的代码看起来像这样:
private void fus(){
synchronized(someRef){
someRef.roh();
}
}
...
private void dah(){
someRef = someOtherRef;
}
我希望发生的事情是:
- 线程 A 进入
fus
,并在调用 roh()
时获取 someref
上的锁。假设 roh
永远不会终止。
- 线程 B 进入
fus
,开始等待 someRef` 空闲,并停留在那里(暂时)。
- 线程C进入
dah
,修改someRef
.
- 现在允许线程 B 进入同步块,因为
someRef
不再引用线程 A 锁定的对象。
实际发生的是:
- 线程 A 进入
fus
,并在调用 roh()
时获取 someref
上的锁。假设 roh
永远不会终止。
- 线程B进入
fus
,找到锁,等待释放(永远)。
- 线程C进入
dah
,修改someRef
.
- 线程 B 继续等待,因为它不再查看
someref
,它正在查看 A 持有的锁。
有没有办法设置它,以便线程 B 重新检查锁以更改引用,或者 "bounce off" 进入其他代码? (类似 sychronizedOrElse 的东西?)
肯定有办法,但 synchronized
不行。推理:在第2个线程进入fus()
的时间点,第一个线程持有someRef
引用的对象的内在锁。重要提示:第二个线程仍然会看到 someRef
引用这个对象,并会尝试获取这个锁。稍后,当第 3 个线程更改引用 someRef
时,它必须以某种方式将此事件通知第 2 个线程。 synchronized
.
不可能做到这一点
据我所知,没有像 synchronized
这样的内置语言功能来处理这种同步。
一种稍微不同的方法是在您的 class 中管理一个 Lock
或给 someRef
一个 Lock
类型的属性。您可以使用 tryLock()
或 tryLock(long timeout, TimeUnit unit)
而不是使用 lock()
。这是我将如何实现它的方案(假设 someRef
具有 Lock
属性):
volatile SomeRef someRef = ... // important: make this volatile to deny caching
...
private void fus(){
while (true) {
SomeRef someRef = this.someRef;
Lock lock = someRef.lock;
boolean unlockNecessary = false;
try {
if (lock.tryLock(10, TimeUnit.MILLISECONDS)) { // I have chonse this arbritrarily
unlockNecessary = true;
someRef.roh();
return; // Job is done -> return. Remember: finally will still be executed.
// Alternatively, break; could be used to exit the loop.
}
} catch (InterruptException e) {
e.printStackTrace();
} finally {
if (unlockNecessary) {
lock.unlock();
}
}
}
}
...
private void dah(){
someRef = someOtherRef;
}
现在,当 someRef
发生变化时,第二个线程将在其下一个周期中看到 someRef
的新值,因此将尝试在新的 Lock
上同步并成功, 如果没有其他线程获取 Lock
.
What actually happens is ... Thread B continues to wait, as it's no longer looking at someref
, it's looking at the lock held by A.
没错。您不能编写代码来同步 变量 。您只能编写代码在某些 object.
上进行同步
线程 B 通过查看变量 someref
找到了要在其上同步的对象,但它只查看该变量一次以找到该对象。 object 是它锁定的对象,直到线程 A 释放对该对象的锁定,线程 B 才会被卡住。
我想在@Turing85
和@james large
.
的优秀答案之上添加更多信息
同意Thread B
继续等待
最好通过使用更好的无锁 API.
来避免 synchronization
这种类型的程序
Atomic
变量具有最小化同步并有助于避免内存一致性错误的功能。
根据您 post 编辑的代码,AtomicReference 似乎是您问题的正确解决方案。
查看 Atomic 软件包的文档页面。
A small toolkit of classes that support lock-free thread-safe programming on single variables. In essence, the classes in this package extend the notion of volatile values, fields, and array elements to those that also provide an atomic conditional update operation of the form:
boolean compareAndSet(expectedValue, updateValue);
SE 中与此主题相关的另一个不错 post。
When to use AtomicReference in Java?
示例代码:
String initialReference = "value 1";
AtomicReference<String> someRef =
new AtomicReference<String>(initialReference);
String newReference = "value 2";
boolean exchanged = someRef.compareAndSet(initialReference, newReference);
System.out.println("exchanged: " + exchanged);
请参阅此 jenkov 教程以获得更好的理解。
我想知道是否有一种简单的方法可以创建一个 synchronized
锁来响应不断变化的引用。我的代码看起来像这样:
private void fus(){
synchronized(someRef){
someRef.roh();
}
}
...
private void dah(){
someRef = someOtherRef;
}
我希望发生的事情是:
- 线程 A 进入
fus
,并在调用roh()
时获取someref
上的锁。假设roh
永远不会终止。 - 线程 B 进入
fus
,开始等待 someRef` 空闲,并停留在那里(暂时)。 - 线程C进入
dah
,修改someRef
. - 现在允许线程 B 进入同步块,因为
someRef
不再引用线程 A 锁定的对象。
实际发生的是:
- 线程 A 进入
fus
,并在调用roh()
时获取someref
上的锁。假设roh
永远不会终止。 - 线程B进入
fus
,找到锁,等待释放(永远)。 - 线程C进入
dah
,修改someRef
. - 线程 B 继续等待,因为它不再查看
someref
,它正在查看 A 持有的锁。
有没有办法设置它,以便线程 B 重新检查锁以更改引用,或者 "bounce off" 进入其他代码? (类似 sychronizedOrElse 的东西?)
肯定有办法,但 synchronized
不行。推理:在第2个线程进入fus()
的时间点,第一个线程持有someRef
引用的对象的内在锁。重要提示:第二个线程仍然会看到 someRef
引用这个对象,并会尝试获取这个锁。稍后,当第 3 个线程更改引用 someRef
时,它必须以某种方式将此事件通知第 2 个线程。 synchronized
.
据我所知,没有像 synchronized
这样的内置语言功能来处理这种同步。
一种稍微不同的方法是在您的 class 中管理一个 Lock
或给 someRef
一个 Lock
类型的属性。您可以使用 tryLock()
或 tryLock(long timeout, TimeUnit unit)
而不是使用 lock()
。这是我将如何实现它的方案(假设 someRef
具有 Lock
属性):
volatile SomeRef someRef = ... // important: make this volatile to deny caching
...
private void fus(){
while (true) {
SomeRef someRef = this.someRef;
Lock lock = someRef.lock;
boolean unlockNecessary = false;
try {
if (lock.tryLock(10, TimeUnit.MILLISECONDS)) { // I have chonse this arbritrarily
unlockNecessary = true;
someRef.roh();
return; // Job is done -> return. Remember: finally will still be executed.
// Alternatively, break; could be used to exit the loop.
}
} catch (InterruptException e) {
e.printStackTrace();
} finally {
if (unlockNecessary) {
lock.unlock();
}
}
}
}
...
private void dah(){
someRef = someOtherRef;
}
现在,当 someRef
发生变化时,第二个线程将在其下一个周期中看到 someRef
的新值,因此将尝试在新的 Lock
上同步并成功, 如果没有其他线程获取 Lock
.
What actually happens is ... Thread B continues to wait, as it's no longer looking at
someref
, it's looking at the lock held by A.
没错。您不能编写代码来同步 变量 。您只能编写代码在某些 object.
上进行同步线程 B 通过查看变量 someref
找到了要在其上同步的对象,但它只查看该变量一次以找到该对象。 object 是它锁定的对象,直到线程 A 释放对该对象的锁定,线程 B 才会被卡住。
我想在@Turing85
和@james large
.
同意Thread B
继续等待
最好通过使用更好的无锁 API.
来避免synchronization
这种类型的程序
Atomic
变量具有最小化同步并有助于避免内存一致性错误的功能。
根据您 post 编辑的代码,AtomicReference 似乎是您问题的正确解决方案。
查看 Atomic 软件包的文档页面。
A small toolkit of classes that support lock-free thread-safe programming on single variables. In essence, the classes in this package extend the notion of volatile values, fields, and array elements to those that also provide an atomic conditional update operation of the form:
boolean compareAndSet(expectedValue, updateValue);
SE 中与此主题相关的另一个不错 post。
When to use AtomicReference in Java?
示例代码:
String initialReference = "value 1";
AtomicReference<String> someRef =
new AtomicReference<String>(initialReference);
String newReference = "value 2";
boolean exchanged = someRef.compareAndSet(initialReference, newReference);
System.out.println("exchanged: " + exchanged);
请参阅此 jenkov 教程以获得更好的理解。