阻止对象属性

时间:2015-09-22 11:50:44

标签: java multithreading concurrency javafx

如果值为null,我写了一个阻塞方法的小类。出于某种原因,它正在抛出一个StackOverflowError,我做错了什么?

public class BlockingObjectProperty<T> extends SimpleObjectProperty<T> {
    public T get() {
        if (super.get() == null) {
            addListener(((observableValue, t, t1) -> {
                synchronized (this) {
                    notifyAll();
                }
            }));
            synchronized (this) {
                try {
                    wait();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e.getMessage(), e);
                }
            }
        }
        return super.get();
    }
}

这是我的测试代码:

BlockingObjectProperty<String> blockingObjectProperty = new BlockingObjectProperty<String>();
new Thread(){
    public void run(){
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        blockingObjectProperty.set("hello world");
    }
}.start();
System.out.println(blockingObjectProperty.get());

以下是例外情况的片段:

Exception in thread "main" java.lang.StackOverflowError
    at com.sun.javafx.binding.ExpressionHelper$SingleChange.<init>(ExpressionHelper.java:144)
    at com.sun.javafx.binding.ExpressionHelper.addListener(ExpressionHelper.java:69)
    at javafx.beans.property.ObjectPropertyBase.addListener(ObjectPropertyBase.java:87)
    at com.neonorb.commons.property.BlockingObjectProperty.get(BlockingObjectProperty.java:8)
    at javafx.beans.binding.ObjectExpression.getValue(ObjectExpression.java:50)
    at com.sun.javafx.binding.ExpressionHelper$SingleChange.<init>(ExpressionHelper.java:152)
    at com.sun.javafx.binding.ExpressionHelper$SingleChange.<init>(ExpressionHelper.java:144)
    at com.sun.javafx.binding.ExpressionHelper.addListener(ExpressionHelper.java:69)
    at javafx.beans.property.ObjectPropertyBase.addListener(ObjectPropertyBase.java:87)
    at com.neonorb.commons.property.BlockingObjectProperty.get(BlockingObjectProperty.java:8)
    at javafx.beans.binding.ObjectExpression.getValue(ObjectExpression.java:50)
    at com.sun.javafx.binding.ExpressionHelper$SingleChange.<init>(ExpressionHelper.java:152)
    at com.sun.javafx.binding.ExpressionHelper$SingleChange.<init>(ExpressionHelper.java:144)
    at com.sun.javafx.binding.ExpressionHelper.addListener(ExpressionHelper.java:69)
    at javafx.beans.property.ObjectPropertyBase.addListener(ObjectPropertyBase.java:87)
    at com.neonorb.commons.property.BlockingObjectProperty.get(BlockingObjectProperty.java:8)

2 个答案:

答案 0 :(得分:2)

当您调用addListener时,JavaFX会询问该属性的当前值(在ExpressionHelper.java:152中),再次调用getValue()。然后 - 因为该值仍然为null - 您无限制地添加另一个侦听器等。

答案 1 :(得分:0)

如果您想等待某个变量变为非空:

private final Object myVarLock = new Object();
private MyType myVar;

MyType get_myVar() {
    synchronized(myVarLock) {
        while (myVar == NULL) {
            myVarLock.wait();
        }
        return myVar;
    }
}

设置变量:

void set_myVar(myType newValue) {
    synchronized(myVarLock) {
        myVar = newValue;
        myVarLock.notifyAll();
    }
}

备注

getter在循环中等待。这对于严格的正确性是必要的,因为Java Langauge Spec允许wait()返回,即使它没有得到通知。 (a.k.a。,虚假唤醒)。

即使您的JVM或应用程序中没有发生虚假唤醒,总是使用循环仍然是明智的。在多个消费者线程彼此竞争以接收事件的任何算法中,循环是必不可少的。该循环的成本不超过if,因此您可能只是习惯于始终使用循环。

myVar的测试和myVar的分配都在synchronized块内。这个很重要。如果他们没有同步,那么可能会发生这种情况:

  • 线程A进入getter,测试myVar并发现它等于NULL。

  • 线程B进入setter,设置myVar非空,调用myVarLock.notifyAll(),返回。通知丢失,因为没有其他线程在等待它。

  • 线程A调用myVarLock.wait()并永远等待一个永远不会再发生的事件。