ConcurrentModificationException при попытке суммировать числа Arraylist, используя несколько потоков в Java - PullRequest
1 голос
/ 28 июня 2019

Я новичок в многопоточности в целом, поэтому до сих пор не до конца понимаю, поэтому не понимаю, почему в моем следующем коде возникают проблемы.Я пытаюсь заполнить ArrayList первыми 1000 числами, а затем суммировать их все, используя три потока.

public class Tst extends Thread {
    private static int sum = 0;
    private final int MOD = 3;
    private final int compare;
    private static final int LIMIT = 1000;
    private static ArrayList<Integer> list = new ArrayList<Integer>();

    public Tst(int compare){
        this.compare=compare;
    }

    public synchronized void populate() throws InterruptedException{
        for(int i=0; i<=Tst.LIMIT; i++){
            if (i%this.MOD == this.compare){
            list.add(i);
            }
        }
    }

    public synchronized void sum() throws InterruptedException{
        for (Integer ger : list){
            if (ger%MOD == this.compare){
                sum+=ger;
            }
        }
    }

    @Override
    public void run(){
        try {
            populate();
            sum();
            System.out.println(sum);
        } catch (InterruptedException ex) {
            Logger.getLogger(Tst.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    public static void main(String[] args) {
        Tst tst1 = new Tst(0);
        tst1.start();
        Tst tst2 = new Tst(1);
        tst2.start();
        Tst tst3 = new Tst(2);
        tst3.start();
    }
}

В теории это должно вывести «500.500», но вместо этого я получаюэто

162241
328741
Exception in thread "Thread-0" java.util.ConcurrentModificationException
    at java.base/java.util.ArrayList$Itr.checkForComodification(ArrayList.java:1042)
    at java.base/java.util.ArrayList$Itr.next(ArrayList.java:996)
    at tst.Tst.sum(Tst.java:38)
    at tst.Tst.run(Tst.java:50)
BUILD SUCCESSFUL (total time: 2 seconds)

Ответы [ 3 ]

2 голосов
/ 29 июня 2019

Проблема возникает из-за того, что ваши методы синхронизируются на «уровне объекта», я имею в виду, что используемая им блокировка монитора относится к определенному объекту (tst1, tst2, tst3). Другими словами, каждый синхронизированный метод использует различную блокировку. В качестве первого шага измените синхронизированные методы на статические.

0 голосов
/ 30 июня 2019

Вы неправильно поняли семантику метода synchronized, каждый из которых использует свой объект блокировки в вашем случае, сделайте это следующим образом:

 class SynchList {
   private int sum = 0;
   private final int MOD = 3;
   private int compare;
   private final int LIMIT = 1000;
   private ArrayList<Integer> list = new ArrayList<Integer>();

   public synchronized void populate( int compare) throws InterruptedException{
       for(int i=0; i<=LIMIT; i++){
           if (i%this.MOD == compare){
           list.add(i);
           }
       }
   }

   public synchronized void sum( int compare ) throws InterruptedException{
       for (Integer ger : list){
           if (ger%MOD == compare){
               sum+=ger;
           }
           System.out.println( sum );
       }
   }
}

class Tst extends Thread {
    int compare;
    SynchList synchList;
    public Tst(int compare, SynchList synchList)
    {
        this.compare= compare;
        this.synchList = synchList;
    }
    @Override
    public void run(){
        try {
            synchList.populate( compare );
            synchList.sum( compare );

        } catch (InterruptedException ex) {
            Logger.getLogger(Tst.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
}

public class Main
{
    public static void main(String[] args) {
        SynchList synchList = new SynchList();

        Tst tst1 = new Tst( 0 , synchList );
        tst1.start();
        Tst tst2 = new Tst( 1, synchList );
        tst2.start();
        Tst tst3 = new Tst( 2, synchList );
        tst3.start();
    }
}
0 голосов
/ 28 июня 2019

пока прогон tst1 подсчитывает сумму в for-each, тогда прогон tst2 может увеличить размер списка. Таким образом, это исключение одновременной модификации. Использование соединения может помочь.

public static void main(String[] args) {
    Tst tst1 = new Tst(0);
    tst1.start();
    tst1.join()
    Tst tst2 = new Tst(1);
    tst2.start();
    tst1.join()
    Tst tst3 = new Tst(2);
    tst3.start();
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...