我在一个项目中有以下设计
- 多个爬虫
- 找到的图像列表
ImageList
(Observable
); 这由线程进程更新(因此是并行的) - 两个监听列表(
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;
}
但是,有时循环会卡住,并且似乎允许对该方法进行第二次调用。然后这就是发生的事情:
- 下载器检索图像 70 到 70
- 下载器检索图像 70 到 71
- 下载器检索图像 70 到 72
- …</li>
- 下载器检索图像 70 到 n
因此,允许对该方法的第二次调用进入该方法,但计数器readImageFrom
永远不会更新。
当我删除对循环中其他函数的两个调用时,脚本开始工作。我知道它们没有同步,但是如果“父级”已经同步,它们是否必须同步?
filter.isOK()
是这样实现的(其他函数只返回 true 或 false;当我hasRightColor
包含代码时代码失败,我猜是因为计算速度有点慢):
public boolean isOk(Image image) {
return hasRightDimensions(image) && hasRightColor(image);
}
这怎么可能发生?Eclipse 不会显示任何抛出的异常(这当然会导致方法退出)。
也许还有一种完全不同的方法可以只从多个观察者那里获取列表的最新内容(每个观察者可能会被通知多次,因为程序并行运行)?