计数器更改时通知线程

时间:2012-03-06 22:32:27

标签: java multithreading concurrency

我正在尝试将一个类设计为我自己的Code Kata,它具有可以设置的value属性,并且该类可以发出ValueListener实例。我们的想法是有一个ValueHolder实例,其中许多客户端线程同时访问它。每个客户端线程都请求了一个ValueWatcher并调用了waitForValue()。

我真正在努力的是我应该在wait()周围的while循环中使用什么条件来避免虚假通知(即值没有改变)。我可以看到这个设计可能使ValueWatcher实例错过更新,但在此阶段我不那么担心。

非常感谢任何提供的指导!

public class ValueHolder {

  private int value = 0;
  private final Object monitor = new Object();

  public void setValue(int value) {
    synchronized(monitor) {
      this.value = value;
      monitor.notifyAll();
    }
  }

  ValueWatcher createChangeWatcher() {
    return new ValueWatcher();
  }

  private class ValueWatcher {
    public int waitForValue() {
      synchronized(monitor) {
        while (==== ??? =====) {
          monitor.wait();
          return value;
        }
      }
    }
  }     
}

5 个答案:

答案 0 :(得分:3)

有趣的问题。这是我头脑中的一个解决方案。拥有版本号以及要更改的值。每当更新值时,版本号也会递增,以便ValueWatcher对象可以检查版本是否上升意味着发生了更改。

修改 我最初有一个AtomicLong,但我从@John Vint窃取了包装对象的想法。

private final VersionValue versionValue = new VersionValue();

public void setValue(int value) {
    synchronized (monitor) {
       versionValue.value = value;
       versionValue.version++;
       monitor.notifyAll();
    }
}

 private class ValueWatcher {
     private long localVersion = 0;
     public int waitForValue() {
         synchronized (monitor) {
             while (true) {
                 if (localVersion < versionValue.version) {
                     // NOTE: the value might have been set twice here
                     localVersion = versionValue.version;
                     return versionValue.value;
                 }
                 monitor.wait();
             }
         }
     }
}

private static class VersionValue {
    int value;
    long version;
}

此外,尽管虚假的唤醒是可能的,但重要的是要记住文本:

  

始终在测试等待条件的循环内调用wait。不要假设中断是针对您正在等待的特定条件,或者条件仍然是真的。

更多关于竞争条件和生产者/消费者模型而不是虚假的唤醒。请参阅my page here about that

答案 1 :(得分:2)

您真正关心的是在输入方法及其同步块后是否更改了值。因此,获取上次更改值的时间戳,并且仅在上次更新的时间戳> 1时继续。然后当你进入。

    private final StampedValue stamped = new StampedValue();

    public void setValue(int value) {
        synchronized (monitor) {
            this.stamped.value = value;
            this.stamped.lastUpdated = System.currentTimeMillis();
            monitor.notifyAll();
        }
    }
    private class ValueWatcher {

        public int waitForValue() { 
          synchronized(monitor) {
               long enteredOn = System.currentTimeMillis();    
               while (enteredOn > stamped.lastUpdated) {
                   monitor.wait();
               }
               return stamped.value;
          }
        }
    }
    private class StampedValue {
        long lastUpdated = System.currentTimeMillis();
        int value;
    }

答案 2 :(得分:1)

每个具有BlockingQueue的侦听器如何作为其作为侦听器注册的一部分给予值设置线程?然后,当值更改时,值设置线程将简单地遍历每个队列,为其提供新值。您可能希望在该循环中使用BlockingQueue.offer,这样如果一个线程尚未准备好接收新值,它将不会阻止其他线程接收它。

这可能不是最有效的方法,但它很简单,并发结构(即硬件)经过充分测试和维护。它也不是那么低效。

答案 3 :(得分:0)

private class ValueWatcher {
  private int oldValue = 0;

  public int waitForValue() {
    synchronized(monitor) {
      while (value == oldValue) {
        monitor.wait();
      }
      oldValue = value
      return oldValue;
    }
  }
}  

答案 4 :(得分:0)

public class ValueHolder {

    private final Object monitor = new Object();
    private LinkedList<WeakReference<ValueWatcher>> waiters = new LinkedList<WeakReference<ValueWatcher>>();

    public void setValue(int value) {
        synchronized (monitor) {
            Iterator<WeakReference<ValueWatcher>> it = waiters.iterator();
            while (it.hasNext()) {
                WeakReference<ValueWatcher> ref = it.next();
                if (ref.get() == null)
                    it.remove();
                else
                    ref.get().waitingList.add(value);
            }
            monitor.notifyAll();
        }
    }

    ValueWatcher createChangeWatcher() {
        ValueWatcher ret = new ValueWatcher();
        synchronized( monitor ) {
            waiters.add(new WeakReference<ValueWatcher>(ret));
        }
        return ret;
    }

    private class ValueWatcher {

        private Queue<Integer> waitingList = new LinkedList<Integer>();

        public int waitForValue() {
            synchronized (monitor) {
                while (waitingList.isEmpty()) {
                    monitor.wait();
                }
                return waitingList.poll();
            }
        }
    }
}

这个想法是你跟踪谁在等待,每个观察者都有一个自上次调用waitForValue()以来已设置的值队列。这样就无需在value中存储ValueHolder,这在ValueWatcher唤醒的时候是一件好事,可能会多次更改。这种方法的缺点是,正如您所看到的那样,创建新的观察者将会阻塞,直到显示器空闲。