重新检查 "synchronized" 锁的可变引用?

Re-Check mutable reference for "synchronized" lock?

我想知道是否有一种简单的方法可以创建一个 synchronized 锁来响应不断变化的引用。我的代码看起来像这样:

private void fus(){
    synchronized(someRef){
        someRef.roh();
    }
}
...
private void dah(){
    someRef = someOtherRef;
}

我希望发生的事情是:

  1. 线程 A 进入 fus,并在调用 roh() 时获取 someref 上的锁。假设 roh 永远不会终止。
  2. 线程 B 进入 fus,开始等待 someRef` 空闲,并停留在那里(暂时)。
  3. 线程C进入dah,修改someRef.
  4. 现在允许线程 B 进入同步块,因为 someRef 不再引用线程 A 锁定的对象。

实际发生的是:

  1. 线程 A 进入 fus,并在调用 roh() 时获取 someref 上的锁。假设 roh 永远不会终止。
  2. 线程B进入fus,找到锁,等待释放(永远)。
  3. 线程C进入dah,修改someRef.
  4. 线程 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 教程以获得更好的理解。