【问题标题】:Concurrent Modification Exception in LinkedListLinkedList 中的并发修改异常
【发布时间】:2013-05-14 10:54:04
【问题描述】:

我正在寻找一种建立有限链表的好方法。如果链表是“满的”,第一个元素将被删除,新的元素将被添加。所以我总是有“最新”的“限制大小”元素。

这是通过以下方式实现的:

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

你知道避免并发修改异常的好方法吗?

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

【问题讨论】:

    标签: java concurrency linked-list summary


    【解决方案1】:

    并发修改问题其实和你对add的修改无关。如果您在计算平均值时添加了一个元素,它也会发生在常规的LinkedList 上。您显示的代码根本无法生成ConcurrentModificationException,这也毫无价值。 (但它可能会给出超出范围的异常......)

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

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

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

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

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

    甚至这个:

    public synchronized doubkle movingAverage() {
        ...
    }
    

    但是,这都是零碎的。更好的方法可能是在更高级别进行同步,或者使用避免显式同步需要的“并发”数据结构。

    【讨论】:

    • 非常感谢!非常有用的回答:)
    【解决方案2】:

    在您的代码示例中,您同步了movingAverage 方法,这意味着对静态方法的访问是线程安全的。但是,您的列表作为传递给它的参数不是。它仍然可以在您通过另一个调用列表的 add 方法的对象检查平均值的同时进行修改。如果您的同步movingAverage() 方法将存在于LimitedLinkedList 对象中,那么对于add 方法而言,该操作将是线程安全的。

    【讨论】:

      【解决方案3】:

      尝试这样的事情(仅用于优化您的代码,它也可能解决您的并发修改异常):

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

      }

      【讨论】:

        【解决方案4】:

        通过Collections.synchronizedList 使用synchronizedList 并按照javadoc 说明进行迭代。

        仅供参考,GitHub 中有一个ConcurrentRunningAverage,您可以使用或用作指南。它扩展了BasicRunningAverge

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2016-11-19
          • 2013-03-12
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多