Подтвердить что ты не робот

Как создать синхронный arraylist

Я создал синхронизированный массивList, подобный этому

import java.text.SimpleDateFormat;
import java.util.*;


class HelloThread  
{

 int i=1;
 List arrayList;
  public  void go()
  {
 arrayList=Collections.synchronizedList(new ArrayList());
 Thread thread1=new Thread(new Runnable() {

  public void run() {
  while(i<=10)
  {
   arrayList.add(i);
   i++;
  }
  }
 });
 thread1.start();
 Thread thred2=new Thread(new Runnable() {
  public void run() {
     while(true)
     {
   Iterator it=arrayList.iterator();
      while(it.hasNext())
      {
       System.out.println(it.next());
      }
     }
  }
 });
 thred2.start();
  }
 }

public class test
{
  public static void main(String[] args)
  {
   HelloThread hello=new HelloThread();
   hello.go();
  }
}

но получая исключение, подобное этому

Исключение в потоке "Thread-1" java.util.ConcurrentModificationException

что-то не так в моем подходе?

4b9b3361

Ответ 1

Iterator из synchronizedList не синхронизируется (и не может быть), вам нужно синхронизировать его вручную при итерации (см. javadoc):

synchronized(arrayList) {
    Iterator it=arrayList.iterator(); 
    while(it.hasNext()) { 
        System.out.println(it.next()); 
   } 
}

Другой подход заключается в использовании CopyOnWriteArrayList вместо Collections.synchronizedList(). Он реализует семантику copy-on-write и поэтому не требует синхронизации.

Ответ 2

Другие ответы выявили проблему:

  • Итераторы для синхронизированных коллекций не синхронизируются. Фактически, это просто итераторы, возвращаемые объектами коллекции внутри классов-оболочек.

  • Многие классы коллекций (включая ArrayList) используют механизм с быстрой задержкой для обнаружения одновременных изменений во время итерации. Это поведение явно задокументировано в javadocs для соответствующих классов. Это то, что вы видите.

Не все классы коллекции делают это. Например, многие из классов коллекции java.util.Concurrent... допускают параллельную модификацию во время итерации, но ослабляют семантику последовательности итераций, так что результаты изменений могут быть или не быть очевидными в объектах, возвращаемых итератором.

В javadoc для Collections.synchronizedList() объясняется, как синхронизировать итератор. В основном вы это делаете:

List list = Collections.synchronizedList(new ArrayList());
  ...
synchronized (list) {
    Iterator i = list.iterator(); // Must be in synchronized block
    while (i.hasNext())
        foo(i.next());
}

(Кроме того, как правило, небезопасно предполагать, что выполнение чего-то подобного будет работать. Теоретически, синхронизированный список может использовать закрытый объект блокировки, а оператор synchronized не будет блокировать одновременные изменения. Однако javadocs скажите, что это то, что нужно делать в этом случае... так что это безопасно.)

Проблема с этим заключается в том, что блокировка коллекции создает потенциальное узкое место concurrency. Альтернативой является использование структуры данных копирования-на-записи, которая внутренне создает копию соответствующих частей коллекции. Этот подход означает, что итератор видит снимок коллекции. Модификации могут быть сделаны в коллекции одновременно с итерацией, но итератор их не видит. Проблема с копированием на запись заключается в том, что модификации потенциально намного дороже.

В конечном счете, вам необходимо сбалансировать характеристики и затраты разных типов коллекций, которые могут быть согласованы по сравнению с вашими фактическими требованиями. Можете ли вы уйти от итератора, не видя всех параллельных изменений?

Ответ 3

Рассмотрите возможность использования CopyOnWriteArrayList, который является потокобезопасным. Каждый раз, когда вы добавляете элемент, создается новая копия базового массива. Тем не менее, итератор не будет отражать добавления к списку с момента создания итератора, но гарантированно не бросать ConcurrentModificationException.

arrayList=new CopyOnWriteArrayList();

Ответ 4

java.util.ConcurrentModificationException возникает, когда вы манипулируете (добавляете, удаляете) коллекцию, итерации по той же коллекции.

Вероятно, вы захотите использовать записи create во втором потоке, пока они были созданы вашим первым потоком. Таким образом, вы можете использовать ArrayLists get( index ) и размер () для управления

Ответ 5

Как сказал Спайк, вы не можете изменять коллекцию при ее повторении. Однако я считаю, что решение заключается в блокировке списка при повторении.

class HelloThread  
{

 int i=1;
 List arrayList;
  public  void go()
  {
 arrayList=Collections.synchronizedList(new ArrayList());
 Thread thread1=new Thread(new Runnable() {

  public void run() {
  while(i<=10)
  {
synchronized(someLock) {
   arrayList.add(i);
}
   i++;
  }
  }
 });
 thread1.start();
 Thread thred2=new Thread(new Runnable() {
  public void run() {
     while(true)
     {
synchronized(someLock) {
   Iterator it=arrayList.iterator();
      while(it.hasNext())
      {
       System.out.println(it.next());
      }
}
     }
  }
 });
 thred2.start();
  }
 }

public class test
{
  public static void main(String[] args)
  {
   HelloThread hello=new HelloThread();
   hello.go();
  }
}

Я не уверен, что вы пытаетесь сделать, поэтому я надеюсь, что это не нарушит функциональность вашего кода.

Ответ 6

Вы не можете изменять коллекцию, которую вы выполняете. Вы можете обойти это, обратившись к элементам массива по индексу, а не через Iterator. Я могу дать больше советов, если вы сообщите мне о проблеме, которую вы пытаетесь решить с помощью этого кода.

Ответ 7

Возьмем обычный список (реализуемый классом ArrayList) и сделаем его синхронизированным. Это показано в классе SynchronizedArrayList.   Мы передаем метод Collections.synchronizedList новый ArrayList of Strings. Метод возвращает синхронизированный список строк.   // Здесь представлен класс SynchronizedArrayList   пакет com.mnas.technology.automation.utility;   import java.util.ArrayList;   import java.util.Collections;   import java.util.Iterator;   import java.util.List;   import org.apache.log4j.Logger;   /**   *   * @author manoj.kumar   * @email [email protected]   *   */   открытый класс SynchronizedArrayList {   static Logger log = Logger.getLogger(SynchronizedArrayList.class.getName());   public static void main (String [] args) {

List<String> synchronizedList = Collections.synchronizedList(new ArrayList<String>());
synchronizedList.add("Aditya");
synchronizedList.add("Siddharth");
synchronizedList.add("Manoj");

// when iterating over a synchronized list, we need to synchronize access to the synchronized list
synchronized (synchronizedList) {
Iterator<String> iterator = synchronizedList.iterator();
while (iterator.hasNext()) {
log.info("Synchronized Array List Items: " + iterator.next());
}
}

}
}

Notice that when iterating over the list, this access is still done using a synchronized block that locks on the synchronizedList object. 
In general, iterating over a synchronized collection should be done in a synchronized block