私はプロジェクトで次のデザインを持っています
- 複数のクローラー
ImageList
見つかった画像のリスト(Observable
); これはスレッド化されたプロセスによって更新されます(したがって並列)- リストを聞く2人のオブザーバー(
Downloader
およびImagesWindow
); 警告:リストはスレッドによって更新されるため、これらは複数回通知される可能性があります
私は常に最新のエントリのみを取得したかったので、カウンターImageList
を使用して実装しました:
public class ImageList extends Observable {
private final ConcurrentMap<Integer, Image> images = new ConcurrentHashMap<Integer, Image>();
private final AtomicInteger counter = new AtomicInteger(0);
/* There is some more code within here, but its not that important
important is that stuff gets added to the list and the list shall
inform all listeners about the change
The observers then check which is the newest ID in the list (often +1
but I guess I will reduce the inform frequency somehow)
and call (in synchronized method):
int lastIndex = list.getCurrentLastIndex();
getImagesFromTo(myNextValue, lastIndex);
myNextValue = lastIndex + 1;
*/
public synchronized void addToFinished(Image job) throws InterruptedException {
int currentCounter = counter.incrementAndGet();
images.put(currentCounter, job);
this.setChanged();
this.notifyObservers();
}
public synchronized int getCurrentLastIndex() {
return counter.get();
}
public ArrayList<Image> getImagesFromTo(int starting, int ending) {
ArrayList<Image> newImages = new ArrayList<Image>();
Image image;
for (int i = starting; i <= ending; i++) {
image = images.get(i);
if (image != null) {
newImages.add(image);
}
}
return newImages;
}
}
オブザーバー(Downloader
ここ)は、次のようにこのメソッドを使用します。
@Override
public void update(Observable o, Object arg) {
System.out.println("Updated downloader");
if (o instanceof ImageList) {
ImageList list = (ImageList) o;
downloadNewImages(list);
}
}
private synchronized void downloadNewImages(ImageList list) {
int last = list.getCurrentLastIndex();
for (Image image : list.getImagesFromTo(readImageFrom, last)) {
// code gets stuck after this line
if (filter.isOk(image)) {
// and before this line
// [here was a line, but it also fails if I remove it]
}
}
// set the index to the new index
readImageFrom = last + 1;
}
ただし、ループがスタックし、メソッドで2番目の呼び出しが許可されているように見える場合があります。次に、これが起こります:
- ダウンローダーは70から70の画像を取得します
- ダウンローダーは70から71の画像を取得します
- ダウンローダーは70から72の画像を取得します
- …</li>
- ダウンローダーは70からnまでの画像を取得します
したがって、メソッドへの2回目の呼び出しはメソッドへの入力を許可されますが、カウンターreadImageFrom
は更新されません。
ループ内の他の関数への両方の呼び出しを削除すると、スクリプトが機能し始めます。それらが同期されていないことは知っていますが、すでに「親」が同期されている場合は同期されている必要がありますか?
filter.isOK()
このように実装されます(他の関数はtrueまたはfalseを返します。hasRightColor
含めた場合、コードは失敗します。計算が少し遅いためだと思います):
public boolean isOk(Image image) {
return hasRightDimensions(image) && hasRightColor(image);
}
これはどのように起こりますか?Eclipseは、スローされた例外を表示しません(もちろん、これによりメソッドが終了します)。
複数のオブザーバーからリストの最新のコンテンツのみを取得するためのまったく異なるアプローチもあるかもしれません(プログラムが並行して実行されるため、各オブザーバーに複数回通知される可能性があります)。