简体   繁体   English

Java与数组列表的并发(如何处理?)

[英]Java concurrency with arraylists (How to handle this?)

It is true that you get a ConcurrentModificationException error when you try and modify an arraylist when iterating through it, yes? 的确,当您尝试遍历数组列表并对其进行修改时,会收到ConcurrentModificationException错误,是吗? But how does the error still persist if you first remove the element you want to modify, iterate through the list, and then insert the element back? 但是,如果您首先删除要修改的元素,遍历列表,然后再插入该元素,那么错误仍然存​​在吗? I do not understand if the logic is like this why it throws a concurrentmodificationexception. 我不明白逻辑是否像这样,为什么会引发并发修改异常。

This whole program uses the Observer Pattern. 整个程序使用观察者模式。


The purpose of the program is to have multiple text boxes, when you type in one, the others update with the same text. 该程序的目的是拥有多个文本框,当您键入一个文本框时,其他文本框将更新为相同的文本。

Subject - Interface 主题 -界面

public interface Subject {

public void attach(Observer o);
public void detach(Observer o);
public void notifyAllObservers();
}

SubjectImpl - implements Subject SubjectImpl-实现Subject

import java.util.*;

public class SubjectImpl implements Subject {

private List <Observer> observers;

public SubjectImpl(){
    observers = new ArrayList<Observer>();
}


@Override
public void attach(Observer o) {
    observers.add(o);
}

@Override
public void detach(Observer o) {
    observers.remove(o);
}

@Override
public void notifyAllObservers() {
            //Iterating code
    for(Observer o: observers){
        o.update();
    }
}
}

Observer - interface 观察者 -界面

public interface Observer {
public void update();
}

Editor - Implements Observer 编辑器 -实施观察员

import java.awt.*
import java.io.*;
import javax.swing.*;



public class Editor extends JFrame implements DocumentListener, Observer {

private FileContentSubject reference;

    private JScrollPane textAreaScrollPane;
    private JTextArea textArea;


public Editor(FileContentSubject filecontentsubject) throws IOException {
    super("Editor");
    initComponents();

    this.reference = filecontentsubject;
    textArea.getDocument().addDocumentListener(reference);
    textArea.getDocument().putProperty("ownerEditor", this); 
}


private void initComponents(){

    textArea = new JTextArea();       

    //set location, size, visible and defaultcloseoperation here        

    getContentPane().setLayout(new BorderLayout());
    getContentPane().add(textArea, BorderLayout.CENTER);
}



@Override
public void update() {
    textArea.setText(reference.getState()); //Call to update each text box
}


@Override
public void changedUpdate(DocumentEvent e) {
}


@Override
public void insertUpdate(DocumentEvent e) {
}


@Override
public void removeUpdate(DocumentEvent e) {
}
}

File Content System - Acts as concrete subject in Observer (there only exists one) 文件内容系统 -在Observer中充当具体主题(仅存在一个)

import javax.swing.event.*;
import javax.swing.text.*;

public class FileContentSubject implements Subject, DocumentListener {

private String state; //Current, most recent copy of everything

public String getState() {
    return this.state;
}

private SubjectImpl reference;

@Override
public void attach(Observer o) {
    reference.attach(o);
}

@Override
public void detach(Observer o) {
    reference.detach(o);
}

@Override
public void notifyAllObservers() {
    reference.notifyAllObservers();
}

public FileContentSubject() {
    reference = new SubjectImpl();
}

@Override
public void changedUpdate(DocumentEvent arg0) {
}

@Override
public void insertUpdate(DocumentEvent arg0) {

    Document doc = (Document) arg0.getDocument();
    try {
        this.state = doc.getText(0, doc.getLength());
    } catch (BadLocationException e) {
        e.printStackTrace();
    }

    Editor e = (Editor) doc.getProperty("ownerEditor");
    reference.detach(e); 
    notifyAllObservers(); 
    reference.attach(e); 
}

@Override
public void removeUpdate(DocumentEvent arg0) {

   //same as insertUpdate(DocumentEvent arg0) ^^
}

}

I believe your ConcurrentModificationException is only thrown when 3 or more editors are used, right? 我相信您的ConcurrentModificationException仅在使用3个或更多编辑器时抛出,对吗? Anyway, let me try to explain what is happening. 无论如何,让我尝试解释正在发生的事情。

Let's suppose you have 3 Editor instances: "Editor1", "Editor2" and "Editor3". 假设您有3个Editor实例:“ Editor1”,“ Editor2”和“ Editor3”。

This is what happens when you type a text into "Editor1" (or, how I call it, when you "begin the chain of events"): 这是当您在“ Editor1”中键入文本时发生的事情(或者,当您“开始事件链”时,我怎么称呼它):

User types on Editor1 // chain begins
FileContentSubject detaches editor: Editor1
FileContentSubject begin notifyAllObservers(): [Editor2, Editor3]
    Editor2 receives update() to new state

So far, normal behavior, right? 到目前为止,行为正常,对吗? The thing is when Editor2's update() is called, it changes Editor2's textArea , and FileContentSubject is a DocumentListener of that textArea as well (as it was of Editor1's textArea ). 问题是,当Editor2的update()被调用时,它改变Editor2的textArea ,并FileContentSubject DocumentListener是的textArea ,以及(因为它是Editor1的的textArea )。

This triggers a new edition, thus creating a new "chain of events". 这将触发一个新版本,从而创建一个新的 “事件链”。 In practice, here's what will happen (repeating from the beginning): 在实践中,将发生以下情况(从头开始重复):

User types on Editor1 // chain begins
FileContentSubject detaches editor: Editor1
FileContentSubject begin notifyAllObservers(): [Editor2, Editor3] // first notification
    Editor2 receives update() to new state // another chain begins! (the 1st chain didn't end yet!)
        FileContentSubject detaches editor: Editor2
        FileContentSubject begin notifyAllObservers(): [Editor3]
            Editor3 receives update() to new state // chain THREE!!
                FileContentSubject detaches editor: Editor3
                FileContentSubject begin notifyAllObservers(): [] // empty list... nobody to notify
                FileContentSubject reattaches editor: Editor3
                FileContentSubject ended notifyAllObservers(): [Editor3] // chain 3 over
        FileContentSubject reattaches editor: Editor2
        FileContentSubject ended notifyAllObservers(): [Editor3, Editor2] // chain 2 over

This is the point where ConcurrentModificationException is thrown. 这是抛出ConcurrentModificationException的地方。 But where exactly is it throw, on the code? 但是代码到底在哪里扔呢?

Actually, it's on SubjectImpl (!): 实际上,它在SubjectImpl (!)上:

@Override
public void notifyAllObservers() {
    for (Observer o : observers) {
        o.update();
    }
}

What is the problem? 问题是什么?

The thing is: 事情是:

  • Notice that in the line with the // first notification comment there's a call to notifyAllObservers() 注意,在// first notification注释行中,有一个对notifyAllObservers()的调用
  • At his moment the list of attached observers is (in this order) [Editor2, Editor3]. 目前,附属观察者的列表是(按此顺序)[Editor2,Editor3]。
  • notifyAllObservers() , when called, then, begins iterating on that list notifyAllObservers() ,然后在被调用时开始在该列表上进行迭代
    • the first step of the iteration is to call Editor2.update() . 迭代的第一步是调用Editor2.update()
      • Editor2.update() starts a whole new chain... Editor2.update()启动了一个全新的链...
      • and when that chain ends, the list of attached observers is now [Editor3, Editor2] (notice: the order has changed!) 当该链结束时,附加的观察者列表现在为[Editor3,Editor2](注意:顺序已更改!)
    • when notifyAllObservers() tries to continue its iteration to the next step, it notices that the list has changed and... bam! notifyAllObservers()尝试将其迭代继续进行到下一步时,它会注意到列表已更改,并且...糟糕! ConcurrentModificationException is thrown. 抛出ConcurrentModificationException

Why? 为什么? Because you cannot change a list where you have begun to iterate. 因为您不能在开始迭代的地方更改列表。 (Either using an iterator, or "foreach".) (使用迭代器或“ foreach”。)

The solution: 解决方案:

There may be many, but the simplest is just to stop the "chain of events". 可能有很多,但是最简单的就是停止“事件链”。 To do that, just change update() on Editor from: 为此,只需将Editor上的update()从以下位置更改:

@Override
public void update() {
    textArea.setText(reference.getState()); // Call to update each text box
}

To: 至:

@Override
public void update() {
    textArea.getDocument().removeDocumentListener(reference);
    textArea.setText(reference.getState()); // Call to update each text box
    textArea.getDocument().addDocumentListener(reference);
}

What does it mean? 这是什么意思? It means that when an Editor changes itself due to anybody else and not some typing on its textArea , he won't notify FileContentSubject (the DocumentListener ) anymore, he'll just change, quietly. 这意味着,当Editor 由于其他人而不是在其textArea上进行某些键入而进行textArea ,他将不再通知FileContentSubjectDocumentListener ),他将安静地进行更改。

This will stop any second chain of events from beginning in the first place. 这将停止从第一位开始的第二个事件链。

Hope I managed to explain the problem! 希望我能解释这个问题! If I can make any point clearer, just let me know! 如果我能说清楚一点,请告诉我! Cheers! 干杯!

Since there are no questions about the code, I'm answering on sentences with question marks. 由于没有关于代码的问题,所以我回答带有问号的句子。

It is true that you get a ConcurrentModificationException error when you try and modify an arraylist when iterating through it, yes? 的确,当您尝试遍历数组列表并对其进行修改时,会收到ConcurrentModificationException错误,是吗?

True. 真正。 To raise ConcurrentModificationException you need to modify a list calling methods on the list and then trying to continue iterating. 要引发ConcurrentModificationException,您需要修改列表上的调用方法的列表,然后尝试继续进行迭代。

But how does the error still persist if you first remove the element you want to modify, iterate through the list, and then insert the element back? 但是,如果您首先删除要修改的元素,遍历列表,然后再插入该元素,那么错误仍然存​​在吗? I do not understand if the logic is like this why it throws a concurrentmodificationexception. 我不明白逻辑是否像这样,为什么会引发并发修改异常。

When you create iterator with List.iterator() you create a contract with List that you will do all modifications to the List with the iterator object you just created. 使用List.iterator()创建迭代器时,将使用List创建一个合约,您将使用刚创建的迭代器对象对List进行所有修改。

If you remove an element directly - List.remove() - you break the contract. 如果直接删除一个元素-List.remove()-则会违反合同。

If you add an element directly - List.add() - you break the contract. 如果直接添加一个元素-List.add()-则会违反合同。

List or list iterator do not care if you modify the element itself. 列表或列表迭代器不在乎是否修改元素本身。

Hope, that will clarify things for you. 希望,这将为您澄清事情。

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

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