我正在寻找一种好方法,建立一个有限的链表。如果链接列表为“full”,则将删除第一个元素,并添加新元素。所以我总是拥有“最新”的“限制大小”元素。
这是通过以下方式实现的:
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;
}
您是否知道避免并发修改异常的好方法?
我唯一的想法是,每次更改列表时计算平均值,所以当我想获得平均值时,我不必迭代它。
答案 0 :(得分:5)
并发修改问题实际上与您对add
的修改无关。如果您在计算平均值时添加了元素,那么它也会以常规LinkedList
发生。您展示的代码根本无法生成ConcurrentModificationException
也是值得的。 (但它可以提供越界异常......)
您遇到问题的最可能原因是您的add
和movingAverage
方法无法正确同步:
synchronized
实例方法锁定目标对象;即列表实例。static synchronized
方法锁定方法声明类的Class
对象;即宣布您的movingAverage
方法的类。如果两个线程没有锁定同一个对象,它们将不会同步,并且您不会互相排斥。这意味着add
和movingAverage
可以同时读取和更新相同的列表...导致异常(或更糟)。
避免这些问题的一种方法可能是将movingAverage
方法更改为:
public static double movingAverage(
LinkedList<AverageObject> valueList) {
synchronized (valueList) {
...
}
}
甚至是这样:
public synchronized doubkle movingAverage() {
...
}
然而,这都是一顿饭。更好的方法可能是在更高级别进行同步,或使用“并发”数据结构,避免显式同步。
答案 1 :(得分:2)
在您的代码示例中,您同步了movingAverage方法,这意味着对静态方法的访问是线程安全的。但是,您的列表作为传递给它的参数不是。它仍然可以在您通过调用列表的add方法的另一个对象检查平均值的同时进行修改。如果您的synchronized movingAverage()方法将存在于LimitedLinkedList对象中,则该操作对于add方法将是线程安全的。
答案 2 :(得分:1)
通过Collections.synchronizedList
使用synchronizedList,然后按照javadoc说明进行迭代。
仅供参考,在GitHub中有一个ConcurrentRunningAverage可以使用或用作指南。它扩展了BasicRunningAverge。
答案 3 :(得分:1)
尝试这样的事情(仅用于优化代码,它也可以解决并发修改异常):
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;
}
}