4

此类扩展Thread,一旦创建,线程就会启动。这是代码:

class Controller extends Thread implements ConfigurationObserver{

    private int refreshMS;

    //...

    @Override
    public void notifyConfiguration(ConfigurationModel config) {
        refreshMS = config.getRefreshMs();
    }

    @Override
    public void run() {
        //...
        while (true) {

            try {
                Thread.sleep(refreshMS);
            } catch (InterruptedException ex) {
                //...
            }
        }
    }
}

它遵循观察者模式。这个类将订阅它自己,ConfigurationController每当任何配置参数发生变化时,都会通过notifyConfiguration(...)方法通知他。

让我对此有点不安全的是属性refresMS。配置通过 GUI (Thread #1) 更改,并影响从Controller该类的运行线程 (Thread #3) 读取的类 (Thread #2) 的属性。

Q1:这会成为比赛条件吗?
Q2:如果是这样,解决这个问题的最佳方法是什么?

4

2 回答 2

3

Q1:这会成为比赛条件吗?

是的。有点。该run()方法最终可能会使用一个refreshMS过时的值。

Q2:如果是这样,解决这个问题的最佳方法是什么?

这最小化了竞争条件:

class Controller extends Thread implements ConfigurationObserver{
    private int refreshMS;
    public synchronized void notifyConfiguration(ConfigurationModel config) {
        refreshMS = config.getRefreshMs();
    }

    public void run() {
        while (true) {
            ...
            synchronized (this) {
                rms = refreshMS;
            }
            Thread.sleep(rms);
            ....
        }
    }
}

如果不在同步块sleep进行调用,就无法完全消除竞争条件。(这会导致调用阻塞的线程可能无限长的时间。坏主意。)notifyConfiguration


现在,这一切都很好,但您还应该问自己,竞争条件是否可能对应用程序的执行产生有害影响。

于 2012-06-12T11:17:42.400 回答
2

我所做的解决方案是 pingw33n 建议的解决方案。使用关键字volatile.

class Controller extends Thread implements ConfigurationObserver{

    private volatile int refreshMS;
    //...
 }

引自Brian Goetz 的管理波动性

易失性变量具有同步的可见性特征,但没有原子性特征。这意味着线程将自动查看 volatile 变量的最新值。

这意味着volatile可以synchronized在极少数情况下使用它来代替。但幸运的是,这是其中之一,因为int它是以原子方式编写的(没有线程可以读取它的值,而其他线程正在修改它)。

因此,正如 Stephen C 所说,这并没有消除竞争条件,它只会让它真的很少发生。在我的情况下,如果refresMS正在运行的线程用旧值读取它并不是什么大问题(如果它几乎不会发生)。

于 2012-06-13T16:54:25.077 回答