我正在寻找一个好方法,建立一个有限的链表。如果链表是"满的",第一个元素将被删除,新的元素将被添加。所以我总是有"最新的" "limit-size"元素
实现方式如下:
private int maxSize;
public LimitedLinkedList(int maxSize) {
this.maxSize = maxSize;
}
@Override
public synchronized boolean add(E object) {
boolean success = super.add(object);
while (this.size() >= maxSize) {
removeFirst();
}
return success;
}
现在我有以下问题:我需要计算链表的平均值。这是我随机得到并发修改异常或索引越界异常的时刻。我的平均方法:
public synchronized static double movingAverage(
LinkedList<AverageObject> valueList) {
if (valueList.isEmpty()) {
return 0;
}
double sum = 0;
int m = 0;
for (int i = 0; i < valueList.size(); i++) {
AverageObject object= valueList.get(i);
sum += object.value;
m++;
}
sum = (m != 0) ? sum / m : sum;
return sum;
}
你知道避免并发修改异常的好方法吗?
我唯一的想法是,计算平均值,每次列表被改变,所以我不需要遍历它,当我想要得到平均值
并发修改问题实际上与您对add
的修改无关。这也会发生在常规的LinkedList
中…如果你在计算平均值时添加了一个元素。您所展示的代码根本不能生成ConcurrentModificationException
,这一点也不值得。(但它可能会给出越界异常…)
您在这里遇到问题的最有可能的原因是您的add
和movingAverage
方法没有正确同步:
-
static synchronized
方法为该方法的声明类锁定Class
对象;即声明movingAverage
方法的类。
synchronized
实例方法锁定目标对象;即列表实例。如果两个线程不锁定同一个对象,它们就不会同步,你就不会得到互斥。这意味着add
和movingAverage
可以同时读取和更新同一个列表…导致异常(或更糟)。
movingAverage
方法改为:
public static double movingAverage(
LinkedList<AverageObject> valueList) {
synchronized (valueList) {
...
}
}
或者
public synchronized doubkle movingAverage() {
...
}
然而,这些都是零碎的。更好的方法可能是在更高的级别上同步,或者使用避免显式同步的"并发"数据结构。
在您的代码示例中,您同步了movingAverage方法,这意味着访问静态方法是线程安全的。但是,作为传递给它的参数的列表不是。它仍然可以在检查平均值的同时被另一个对象修改,该对象调用列表的add方法。如果您的同步movingAverage()方法将在LimitedLinkedList对象中存在,那么关于add方法的操作将是线程安全的。
尝试这样做(仅用于优化代码,它还可以解决并发修改异常):
public class LimitedLinkedList extends LinkedList<AverageObject>{
private int maxSize;
private int sum = 0;
public LimitedLinkedList(int maxSize) {
this.maxSize = maxSize;
}
@Override
public synchronized boolean add(AverageObject object) {
sum = sum + object.value;
boolean success = super.add(object);
while (this.size() >= maxSize) {
sum = sum - getFirst().value;
removeFirst();
}
return success;
}
public synchronized double movingAverage(int sum, int length) {
double avegage = (sum != 0) ? sum / length : sum;
return sum;
}
}
通过Collections.synchronizedList
使用synchronizedList并遵循javadoc指令进行迭代。
仅供参考,有一个ConcurrentRunningAverage检查到GitHub,你可以使用或使用作为一个指南。它扩展了basicrunningaverage