Почему я получаю CurrentModificationException без удаления во время итерации? - Java - PullRequest
0 голосов
/ 09 сентября 2018

Я не использую потоки, но постоянно получаю сообщение об ошибке:

"Исключение в потоке" AWT-EventQueue-0 "java.util.ConcurrentModificationException"

Вот мой код:

@Override
public void paint(Graphics g) {
    for(Rectangles emp: shapes.list) {
        //Loop through all rectangle objects
        for(int[] temp: emp.arr) {
            //Loop through each objects array
            g.drawRect(temp[0], temp[1], 20, 20);
    g.drawRect(20, 20, 20, 20);
        }
    }
}

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

public class Shapes {
LinkedList<Rectangles> list  = new LinkedList<Rectangles>();
Random rand = new Random();
void newshape() {
    int shape = rand.nextInt(7);
    switch(shape) {
        case 0:
            list.add(makeSquare());
            break;
        case 1:
            list.add(makeLine());
            break;
        case 2:
            list.add(makeTShape());
            break;
        case 3:
            list.add(makeLShape());
            break;
        case 4:
            list.add(makeJShape());
            break;
        case 5:
            list.add(makeZShape());
            break;
        case 6:
            list.add(makeSShape());
            break;
        }
    }

Я использую игровой цикл Notch, где метод tick () вызывает метод newshape (), если с момента последнего создания новой фигуры прошло одну секунду. Затем вызывается метод рисования.

public void run(Game game) {
    while(true) {
        lastshapemake = System.nanoTime();
        long lastTime = System.nanoTime();
        double Target_FPS = 60.0;
        double ns = 1000000000 / Target_FPS;
        double delta = 0;
        while(running) { 
            long now = System.nanoTime();
            delta += (now - lastTime) / ns;
            lastTime = now;
            while(delta >= 1) {
                tick();
                delta--;

            }
            if(running) {
                game.repaint();
            }
        }   
    }
}

Ответы [ 2 ]

0 голосов
/ 09 сентября 2018

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

  1. Ваш главный поток
  2. Тема Swing UI

Swing (и большинство графических структур GUI) являются однопоточными в том смысле, что только один поток должен манипулировать GUI и любыми данными, от которых зависит GUI.

См .: Почему модель потоков Swing считается неправильной и как она должна быть?

Метод repaint не рисует сразу; он помещает событие в очередь событий и, когда это событие захватывается потоком Swing, затем выполняет код перерисовки.

Во время использования метода tick, который вы не опубликовали, вы обновляете тот же список, который вы повторяете в методе paint, и потому что метод paint иногда запускается точно в то же время, что и метод. ваш tick метод, который вызывает исключения ConcurrentModificationExceptions.

Есть несколько способов обойти это.

  1. Вы можете использовать двойную буферизацию, позволить вашему основному потоку обновлять невидимый буфер и позволить методу перерисовки отображать буфер, когда он будет готов.
  2. Вы можете запустить все в потоке Swing, используя EventQueue.invokeLater или javax.swing.Timer (вы можете запланировать свой метод tick таким образом)
  3. Вы можете скопировать список в вашем методе paint перед его повторением. Однако, поскольку вы используете несколько потоков, это может привести к скачкам данных, если вы не примете особые меры предосторожности. Вы можете использовать CopyOnWriteArrayList вместо обычного ArrayList или использовать Collections.synchronizedList, чтобы превратить обычный список в один, чтобы безопасно публиковать изменения в одном потоке в другом.
0 голосов
/ 09 сентября 2018

ConcurrentModificationException не имеет отношения к потокам.

Все это означает: мы находимся в точке 'Z' на следующей временной шкале:

  • В точке X вы извлекаете итератор из коллекции.
  • В точке Y (после X) коллекция кем-то модифицируется (может быть в другом потоке или в вашем собственном потоке, не имеет значения, где это происходит).
  • В точке Z (после Y) вы вызываете любую операцию с итератором, полученную в X.

Обратите внимание, что вы создаете итератор при входе в блок for (Type elem : someCollection) и вызываете операцию next на этом итераторе в любое время, что для циклов цикла.

Из вашего кода невозможно определить, где происходит изменение (вы этого не делаете; что-то такое простое, как collection.remove (x) внутри цикла for, может вызвать ConcurrentModificationException, но вы этого не делаете что во фрагменте ты вставил).

Обратите внимание, что с Swing у вас может быть несколько потоков, даже если вы об этом не думаете. Так что, если этот фрагмент действительно все, что у вас есть, задействуется другой поток, и они изменяют shapes или один из элементов в shapes.

...