[英]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: 事情是:
// first notification
comment there's a call to notifyAllObservers()
// first notification
注释行中,有一个对notifyAllObservers()
的调用 notifyAllObservers()
, when called, then, begins iterating on that list notifyAllObservers()
,然后在被调用时开始在该列表上进行迭代
Editor2.update()
. Editor2.update()
。
Editor2.update()
starts a whole new chain... Editor2.update()
启动了一个全新的链... 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”。)
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
,他将不再通知FileContentSubject
( DocumentListener
),他将安静地进行更改。
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.