Итерируемые проблемы параллелизма в циклическом списке Java - PullRequest
0 голосов
/ 28 ноября 2018

У меня есть несколько случаев, когда мне нужно установить List, а затем перебрать их в циклическом режиме.Я относительно новичок в многопоточности, поэтому я учусь по ходу дела.

Сегодня у меня начались проблемы с параллелизмом.

Exception in thread "LogThing: 25" Exception in thread "LogThing: 27" Exception in thread "LogThing: 21" java.util.ConcurrentModificationException
    at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:901)
    at java.util.ArrayList$Itr.next(ArrayList.java:851)
    at com.google.common.collect.Iterators$2.next(Iterators.java:418)
    at com.example.my.RoundRobinIterable.getNext(RoundRobinIterable.java:22)
    at com.example.my.EpsSendingStrategy.run(EpsSendingStrategy.java:22)
    at java.lang.Thread.run(Thread.java:745)

Теперь RoundRobbinIterable довольно прост.

import java.util.Iterator;
import java.util.List;

import com.google.common.collect.Iterables;

public class RoundRobinIterable<T> {
  private final Iterator<T> elements;

  public RoundRobinIterable(final List<T> elements) {
    this.elements = Iterables.cycle(elements).iterator();
  }

  public synchronized T getNext() {
    return this.elements.next();;
  }

}

Теперь EpsSendingStrategy.java имеет static RoundRobinIterable<PrintWriter> writerIterable, и, как вы можете видеть из трассировки стека, он также является потоком.Когда я создаю RoundRobinIterable, я передаю Collections.synchronizedList(new ArrayList<>()) с некоторыми значениями.Строка 22 оказывается writer = writerIterable.getNext();

Так что каким-то образом getNext() вызывает java.util.ConcurrentModificationException исключение

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

public class EpsSendingStrategy extends SendingStrategy {

  public EpsSendingStrategy(LogDosSettings settings) {
    super(settings);
    logger = LogManager.getLogger(EpsSendingStrategy.class);
  }

  @Override
  public void run() {
    createConnections();
    startSending.set(System.nanoTime());
    PrintWriter writer;

    while (sendMessages) {
      logger.debug("about to write message");
      writer = writerIterable.getNext();
      writer.println(settings.getMessage());
      writer.flush();

      lastSent.set(startSending.get() + logItoration.get() * 1000000000L / settings.getEps());
      while (System.nanoTime() < lastSent.get());

      logItoration.incrementAndGet();
    }
  }

}

Редактировать 2: я также пыталсята же самая синхронизация, использованная на https://stackoverflow.com/a/4493759/2599884, но это также был перебор.Есть нечто более глубокое, чего я не понимаю.

Ответы [ 2 ]

0 голосов
/ 29 ноября 2018

ConcurrentModificationException выбрасывается, потому что вы перебираете список, в то время как некоторые потоки добавляют элемент в список.

Вы используете Collections.synchronizedList , где add иметоды удаления синхронизируются с помощью блокировки списка.Чтобы запретить другим добавлять что-либо в список во время итерации списка с помощью итератора, необходимо заблокировать этот список следующим образом.

synchronized(list){
Iterator i = list.iterator(); 
      while (i.hasNext()){
        //do something
  }
}

Вы не делаете этого, вы синхронизируете getNext метод, но не с блокировкой списка, и вы синхронизируете только при вызове метода next () , которого недостаточно.

 public synchronized T getNext() {
    return this.elements.next();;
  }

Если вы хотите использовать свой шаблонвам нужно создать копию списка в вашем RoundRobinIterable

import java.util.Iterator;
import java.util.List;

import com.google.common.collect.Iterables;

public class RoundRobinIterable<T> {
  private final Iterator<T> elements;

  public RoundRobinIterable(final List<T> elements) {
    List<T> copyOfElements = new ArrayList<>(elements);
    this.elements = Iterables.cycle(copyOfElements).iterator();
  }

  public synchronized T getNext() {
    return this.elements.next();;
  }
}

Теперь никто не может добавить элемент в скопированный список во время итерации списка, но вы не получите элементы, добавленные вОригинальный список после того, как вы создали RoundRobinIterable.Если вы хотите получить элементы, которые добавляются позже, то лучше использовать что-то еще, чем List.Например, ConcurrentLinkedQueue.

0 голосов
/ 28 ноября 2018

Как уже упоминалось в javadoc Collections.synchronizedList () : -

Iterator i = list.iterator(); // Must be in synchronized block

Но вы передаете свой синхронизированный список в guava в этой строке: -

this.elements = Iterables.cycle(elements).iterator();

который Я думаю не использует синхронизацию при вызове .iterator().Кроме того, он может вызывать его в неуправляемое вами время (например, когда вы его модифицируете).

Хотя этот ответ может указывать вам верное направление, возможно, стоит подождать более точного ответа откто-то (я никогда не использовал гуаву).

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