重新检查" synchronized"的可变参考。锁?

时间:2016-05-19 20:35:25

标签: java multithreading synchronized

我想知道是否有一种简单的方法可以使synchronized锁定响应更改的引用。我的代码看起来像这样:

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

我想要发生的是:

  1. 主题A输入fus,并在调用someref时获取roh()上的锁定。假设roh永远不会终止。
  2. 线程B进入fus,开始等待someRef`空闲,并保持原状(暂时)。
  3. 主题C输入dah,并修改someRef
  4. 现在允许线程B进入同步块,因为someRef不再引用对象线程A已锁定。
  5. 实际发生的是:

    1. 主题A输入fus,并在调用someref时获取roh()上的锁定。假设roh永远不会终止。
    2. 线程B进入fus,找到锁,并等待它被释放(永远)。
    3. 主题C输入dah,并修改someRef
    4. 线程B继续等待,因为它不再查看someref,它正在查看A所持的锁定。
    5. 有没有办法设置它,以便线程B重新检查锁定以更改引用,或者"反弹"进入其他代码? (像sychronizedOrElse?)

3 个答案:

答案 0 :(得分:3)

肯定有办法,但不是synchronized。推理:在第二个线程进入fus()的时间点,第一个线程保存someRef引用的对象的内部锁。重要提示:第​​二个线程仍会看到someRef引用此对象,并将尝试获取此锁。稍后,当第3个线程更改引用someRef时,它必须以某种方式通知第二个线程关于此事件。 synchronized无法做到这一点。

据我所知,没有像synchronized这样的内置语言功能来处理这种同步。

有一种不同的方法是在您的班级中管理Lock或为someRef提供Lock类型的属性。您可以使用lock()tryLock(),而无需使用tryLock(long timeout, TimeUnit unit)。这是一个关于如何实现这一点的方案(假设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

答案 1 :(得分:2)

  

实际发生的是......线程B继续等待,因为它不再看someref,它正在查看A持有的锁。

那是对的。您无法编写代码以在变量上进行同步。您只能编写代码以在某些对象上进行同步。

线程B通过查看变量someref找到要同步的对象,但它只查看该变量一次才能找到该对象。 对象是它锁定的内容,并且在线程A释放对该对象的锁定之前,线程B将被卡住。

答案 2 :(得分:1)

我想在@ Turing85和@ james large的优秀答案之外添加更多信息。

我同意Thread B继续等待。

通过使用更好的无锁API,最好避免synchronization这类程序。

Atomic个变量具有最小化同步的功能,有助于避免内存一致性错误。

根据您发布的代码,AtomicReference似乎是您问题的正确解决方案。

查看Atomic包上的文档页面。

  

一个小型工具包,支持对单个变量进行无锁线程安全编程。本质上,此包中的类将volatile值,字段和数组元素的概念扩展为也提供表单的原子条件更新操作的那些:

boolean compareAndSet(expectedValue, updateValue);

SE中与本主题相关的另一篇不错的帖子。

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教程。