[英]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.