LinkedList中的并发修改异常

Fra*_*e91 0 java concurrency summary linked-list

我正在寻找一个好方法,建立一个有限的链表.如果链接列表为"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;
}
Run Code Online (Sandbox Code Playgroud)

现在我有以下问题:我需要计算链表的平均值.这是我随机获取并发修改异常或索引越界异常的时刻.我的平均方法:

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;
 }
Run Code Online (Sandbox Code Playgroud)

您是否知道避免并发修改异常的好方法?

我唯一的想法是,每次更改列表时计算平均值,所以当我想获得平均值时,我不必迭代它.

Ste*_*n C 5

并发修改问题实际上与您的修改无关add.LinkedList如果你在计算平均值的同时添加了一个元素,它也会以常规方式发生.你展示的代码根本无法产生任何东西也是值得的ConcurrentModificationException.(但它可以提供越界异常......)

您遇到问题的最可能原因是您addmovingAverage方法未正确同步:

  • synchronized实例方法锁定目标对象; 即列表实例.
  • 一个static synchronized方法锁定Class的方法的声明类对象; 即声明movingAverage方法的类.

如果两个线程没有锁定同一个对象,它们将不会同步,并且您不会互相排斥.这意味着add并且movingAverage可以同时读取和更新相同的列表...导致异常(或更糟).

避免这些问题的movingAverage一种方法可能是将方法更改为:

public static double movingAverage(
    LinkedList<AverageObject> valueList) {
    synchronized (valueList) {
       ...
    }
}
Run Code Online (Sandbox Code Playgroud)

甚至这个:

public synchronized doubkle movingAverage() {
    ...
}
Run Code Online (Sandbox Code Playgroud)

然而,这都是零食.更好的方法可能是在更高级别进行同步,或使用"并发"数据结构,避免显式同步的需要.