簡體   English   中英

計數器更改時通知線程

[英]Notify Threads When Counter Changes

我正在嘗試為自己設計一個類作為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;
        }
      }
    }
  }     
}

有趣的問題。 這是我頭腦中的一個解決方案。 擁有版本號以及要更改的值。 每當更新值時,版本號也會遞增,因此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。 不要假設中斷是針對您正在等待的特定條件,或者條件仍然是真的。

更多關於競爭條件和生產者/消費者模型而不是虛假的喚醒。 請看我的頁面

您真正關心的是在輸入方法及其同步塊后是否更改了值。 因此,請記錄上次更改值的時間戳,並且僅在上次更新的時間戳>>然后輸入時繼續。

    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;
    }

每個具有BlockingQueue的偵聽器如何將其作為注冊為偵聽器的一部分提供給值設置線程? 然后,當值更改時,值設置線程將簡單地遍歷每個隊列,為其提供新值。 您可能希望在該循環中使用BlockingQueue.offer ,這樣如果一個線程尚未准備好接收新值,它將不會阻止其他線程接收它。

這可能不是最有效的方法,但它很簡單,並發結構(即硬部分)經過了充分的測試和維護。 它也不是那么低效。

private class ValueWatcher {
  private int oldValue = 0;

  public int waitForValue() {
    synchronized(monitor) {
      while (value == oldValue) {
        monitor.wait();
      }
      oldValue = value
      return oldValue;
    }
  }
}  
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()以來已設置的值隊列。 這消除了在ValueHolder存儲value的需要,這在ValueWatcher喚醒時是一件好事,可能已經多次改變。 這種方法的缺點是,正如您所看到的那樣,創建新的觀察者將會阻塞,直到監視器空閑。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM