简体   繁体   English

LinkedList中的并发修改异常

[英]Concurrent Modification Exception in LinkedList

I'm looking for a good way, to build a limited linked list. 我正在寻找一种建立有限链接列表的好方法。 If the linked list is "full", the first element will be removed and the new one will be added. 如果链接列表为“完整”,则第一个元素将被删除,而新元素将被添加。 So I always have the "newest" "limit-size" elements. 因此,我始终具有“最新”“限制大小”元素。

This is implemented in the following way: 这是通过以下方式实现的:

    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;
}

Now I have the following problem: I need to calculate the average of the linked-list. 现在,我遇到以下问题:我需要计算链表的平均值。 This is the moment, where I randomly get the concurrent modification exception or an index-out-of-bounds exception. 此时此刻,我随机获得并发修改异常或索引越界异常。 My method for average: 我的平均方法:

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;
 }

Do you know a good way to avoid concurrent modification exceptions? 您知道避免并发修改异常的好方法吗?

My only idea is, to calculate the average, everytime the list is changed, so I don't have to iterate through it, when I want to have the average. 我唯一的想法是,每次更改列表时都要计算平均值,所以当我想要平均值时,不必遍历该平均值。

The concurrent modification problem is actually nothing to do with your modifications to add . 并发修改问题实际上与您要add的修改无关。 It would occur with a regular LinkedList too ... if you added an element while computing the average. 如果您在计算平均值时添加了元素,也会在常规LinkedList发生。 It is also worth nothing that the code you showed cannot generate a ConcurrentModificationException at all. 您显示的代码根本无法生成ConcurrentModificationException也是ConcurrentModificationException的。 (But it could give out-of-bounds exceptions ...) (但它可能会超出范围……)

The most likely reason you are having problems here is that your add and movingAverage methods are not synchronizing correctly: 您在此处遇到问题的最可能原因是您的addmovingAverage方法未正确同步:

  • A synchronized instance method locks the target object; synchronized实例方法锁定目标对象。 ie the list instance. 即列表实例。
  • a static synchronized method locks the Class object for the method's declaring class; static synchronized方法将Class对象锁定为该方法的声明类; ie the class that declares your movingAverage method. 即声明您的movingAverage方法的类。

If two threads don't lock the same object, they won't synchronize, and you won't get mutual exclusion. 如果两个线程不锁定同一对象,则它们将不会同步,并且您将不会互相排斥。 This means that the add and movingAverage could be simultaneously reading and updating the same list ... resulting in an exception (or worse). 这意味着addmovingAverage可能会同时读取和更新同一列表...导致异常(或更糟)。

One way to avoid these problems might be to change the movingAverage method to this: 避免这些问题的一种方法可能是将movingAverage方法更改为:

public static double movingAverage(
    LinkedList<AverageObject> valueList) {
    synchronized (valueList) {
       ...
    }
}

or even this: 甚至这个:

public synchronized doubkle movingAverage() {
    ...
}

However, this is all piece-meal. 但是,这都是零碎的。 A better approach might be to synchronize at a higher level, or use a "concurrent" data structure that avoids the need for explicit synchronizing. 更好的方法可能是在更高级别进行同步,或者使用避免需要显式同步的“并发”数据结构。

In your code example you synchronized the movingAverage method which means that access to the static method is thread safe. 在您的代码示例中,您同步了movingAverage方法,这意味着对静态方法的访问是线程安全的。 However, your list as the parameter passed to it is not. 但是,您的列表作为传递给它的参数却没有。 It can still be modified at the same time as you check for the averages by another object that calls the add method of the list. 在您通过另一个调用列表的add方法的对象检查平均值时,仍可以同时修改它。 If your synchronized movingAverage() method would live within the LimitedLinkedList object then the operation would be thread safe in regards to the add method. 如果您的同步movingAverage()方法位于LimitedLinkedList对象中,则该操作对于add方法而言将是线程安全的。

Use a synchronizedList via Collections.synchronizedList and follow the javadoc instructions for iteration. 使用synchronizedList通过Collections.synchronizedList ,并按照指示的javadoc迭代。

FYI, there is a ConcurrentRunningAverage checked into GitHub that you could use or use as a guide. 仅供参考,有一个ConcurrentRunningAverage已签入GitHub,您可以将其用作指南。 It extends BasicRunningAverge . 它扩展了BasicRunningAverge

Try something like this ( only for optimizing your code, It may also solve your concurrent modification exception): 尝试这样的事情(仅用于优化代码,它也可能解决您的并发修改异常):

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;
 }

} }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM