LinkedList并发修改异常



我正在寻找一个好方法,建立一个有限的链表。如果链表是"满的",第一个元素将被删除,新的元素将被添加。所以我总是有"最新的" "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,这一点也不值得。(但它可能会给出越界异常…)

您在这里遇到问题的最有可能的原因是您的addmovingAverage方法没有正确同步:

    synchronized实例方法锁定目标对象;即列表实例。
  • static synchronized方法为该方法的声明类锁定Class对象;即声明movingAverage方法的类。

如果两个线程不锁定同一个对象,它们就不会同步,你就不会得到互斥。这意味着addmovingAverage可以同时读取和更新同一个列表…导致异常(或更糟)。

避免这些问题的一种方法可能是将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

相关内容

  • 没有找到相关文章

最新更新