Java Как добавить в список массивов во время цикла

package biz.boulter.state;

import java.awt.Color;
import java.awt.Graphics2D;
import java.util.ArrayList;

import biz.boulter.sword.Game;
import biz.boulter.sword.Particle;

public class Menu implements State{
private ArrayList<Particle> particles = new ArrayList<Particle>();
boolean inLoop = false;

public Menu(){

}

@Override
public void render(Graphics2D g) {
    if(!inLoop){
        for(Particle p: particles){
            p.render(g);
        }
    }
}

@Override
public void tick() {
    if(!inLoop){
        for(Particle p: particles){
            p.tick();
        }
    }
}

@Override
public void keyPressed(int kc) {

}

@Override
public void keyReleased(int kc) {

}

@Override
public void mousePressed(int button, int x, int y) {
    for(int i = 0; i<500; i++){
        int rand;
        if(Game.rand.nextBoolean()){
            rand = Game.rand.nextInt(10)-11;
        }else{
            rand = Game.rand.nextInt(10)+1;
        }
        particles.add(new Particle(x, y, rand, Game.rand.nextInt(10)-11, new Color(Game.rand.nextInt(1000000000))));
    }
}

@Override
public void mouseReleased(int button, int x, int y) {

}
}

Привет, ребята, это мой код, и я продолжаю получать ConcurrentModificationException. И я знаю, что это означает, что две вещи изменяют переменную частиц одновременно. Но как еще я должен добавить в список массивов. Я видел некоторые форумы, в которых говорилось, что используйте итератор, но это для удаления, а не для добавления.

Заранее спасибо!

РЕДАКТИРОВАТЬ:

трассировки стека:

Exception in thread "Thread-2" java.util.ConcurrentModificationException
    at java.util.ArrayList$Itr.checkForComodification(Unknown Source)
    at java.util.ArrayList$Itr.next(Unknown Source)
    at biz.boulter.state.Menu.render(Menu.java:21)
    at biz.boulter.sword.Game.render(Game.java:43)
    at biz.boulter.sword.Game.run(Game.java:136)
at java.lang.Thread.run(Unknown Source)

класс частиц:

package biz.boulter.sword;

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Rectangle;

public class Particle {
private double x = 0;
private double y = 0;
private double xa = 0;
private double ya = 0;
private Color particleColour;
private Rectangle img;

public Particle(int x, int y, int xa, int ya, Color colour){
    this.x = x;
    this.y = y;
    this.xa = xa;
    this.ya = ya;
    this.particleColour = colour;
    img = new Rectangle(x, y, 5, 5);
}

public void render(Graphics2D g){
    img.setBounds((int)Math.round(x), (int)Math.round(y), 5, 5);
    g.setColor(particleColour);
    g.fill(img);
}

public void tick(){
    ya+=0.5;

    if(xa < 0){
        xa+=1;
    }

    if(xa > 0){
        xa-=1;
    }

    x+=xa;
    y+=ya;
}
}

person nedb    schedule 05.05.2014    source источник
comment
где ловишь ConcurrentModificationException, покажи трассировку стека.   -  person alex2410    schedule 05.05.2014
comment
Если событие mousePressed перехвачено во время выполнения tick() или render(), будет предпринята попытка добавления во время прохождения списка, что является недопустимым. Вам нужно реализовать некоторый механизм безопасности параллелизма. Ищите synchronized, семафоры...   -  person cangrejo    schedule 05.05.2014
comment
хорошо, я добавил трассировку стека   -  person nedb    schedule 05.05.2014
comment
Покажите коды методов Particle.tick() и Particle.render(). Я думаю, что проблема в этом.   -  person renz    schedule 05.05.2014


Ответы (4)


Просто, чтобы избежать параллельных проблем и ConcurentModificationException в итерациях по коллекциям, вы должны использовать параллельные классы коллекций.

Например, для ArrayList вы можете попробовать CopyOnWriteArrayList, который является поточно-ориентированным вариантом ArrayList.

Подробнее об этом читайте здесь: http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/CopyOnWriteArrayList.html

person Bosko Mijin    schedule 05.05.2014

У вас есть 2 варианта. Если можно, пометьте методы tick, render и mousePressed как synchronized. Это решит проблему, но это может быть не лучшим решением, если есть вставки с более высокой скоростью.

Другим решением было бы изменить

private ArrayList<Particle> particles = new ArrayList<Particle>(); 

to

private LinkedBlockingQueue<Particle> particles = new LinkedBlockingQueue<Particle>();

Это работает, потому что согласно документации метода iterator:

Возвращенный итератор является «слабо согласованным» итератором, который никогда не вызовет исключение ConcurrentModificationException и гарантирует обход элементов в том виде, в каком они существовали при построении итератора, и может (но не обязательно) отражать любые модификации после построения.

person Jatin    schedule 05.05.2014

Самое простое, что можно сделать, это заблокировать коллекцию, когда вы ее используете.

synchronized(particles) {
    for(Particle p: particles){
        p.tick();
    }
}

и

Particle p = new Particle(x, y, rand, Game.rand.nextInt(10)-11, new Color(Game.rand.nextInt(1000000000)))
synchronized(particles) {
    particles.add(p);
}

Таким образом, вы гарантируете, что доступ не является одновременным.

Проблема с CopyOnWriteArrayList заключается в том, что операции записи обходятся дорого. Как следует из названия, он копирует весь список при каждом обновлении.

Проблема с BlockingQueue в том, что он не предназначен для повторения. Будет работать, но не так эффективно.

person Peter Lawrey    schedule 05.05.2014

вы пытаетесь изменить внутреннее состояние массива, во время цикла это ожидаемое поведение.

Один быстрый (но приводит к некоторым затратам на производительность) - использовать затем CopyOnWriteArrayList, см. связанную запись, В каких ситуациях подходит CopyOnWriteArrayList?

person javapapo    schedule 05.05.2014