2017-02-02 11 views
0

Только что начал изучать многопоточность и застрял в ситуации для одновременной модификации.ConcurrentModificationException в доступе с несколькими потоками

Вот мой Java Class

package ashish.demo.threading.basic; 

import java.util.ArrayList; 
import java.util.ConcurrentModificationException; 
import java.util.List; 

/** 
* Created by ashishratan on 2/2/17. 
*/ 
public class ItemTask implements Runnable { 

    private volatile boolean shutdown; 
    private List<Item> itemList = new ArrayList<Item>(); 
    private volatile Item item; 
    private volatile boolean addItemEvent; 
    private volatile boolean removeItemEvent; 

    @Override 
    public void run() { 
     while (!this.shutdown) { 
      try { 
       synchronized (this) { 
        if (this.item != null) { 
         this.item.setProductName("Created By:: " + Thread.currentThread().getName()); 
        } 
        if (this.addItemEvent) { 
         this.itemList.add(this.item); 
         this.item=null; 
         this.addItemEvent = false; 
         this.statusDisplay(); 

        } 
        if (this.removeItemEvent) { 
         this.itemList.add(this.item); 
         this.removeItemEvent = false; 
         this.statusDisplay(); 
        } 
       } 
       Thread.sleep(100); 
      } catch (InterruptedException e) { 
       e.printStackTrace(); 
      } 
     } 

     System.out.println("Shutting down..."); 
    } 


    public void addItem(Item item) { 
     this.item = item; 
     this.addItemEvent = true; 
    } 

    public synchronized List<Item> getItemList() { 
     this.statusDisplay(); 
     return itemList; 
    } 

    public void setItemList(List<Item> itemList) { 
     this.itemList = itemList; 
    } 

    public synchronized void shutdownHook() { 
     this.statusDisplay(); 
     this.shutdown = true; 
     System.out.println(this.getItemList()); 
    } 

    private synchronized void statusDisplay() { 

     System.out.println(Thread.currentThread()); 
     System.out.println("Total Items In Stock are " + this.itemList.size()); 
    } 
} 

Runner Класс

package ashish.demo.threading; 

    import ashish.demo.threading.basic.Item; 
    import ashish.demo.threading.basic.ItemTask; 

    import java.util.Scanner; 

    public class Main { 

     public static void main(String[] args) { 

      ItemTask itemTask = new ItemTask(); 
      Thread thread =null; 

      for (int i = 0; i < 500; i++) { 
       thread=new Thread(itemTask); 
       thread.setName("ItemTask-Thread-"+(i+1)); 
       thread.setPriority(Thread.MAX_PRIORITY); 
       thread.start(); 
      } 
      System.out.println("Please Enter Number (0) to exit"); 
      Scanner scanner = new Scanner(System.in); 
      int i = scanner.nextInt(); 
      while (i>0){ 
       itemTask.addItem(new Item(1,12.0f,"Product "+i,(byte)12)); 
       System.out.println(itemTask.getItemList()); // Line #26, Exception 
       System.out.println("Please Enter Number (0) to exit"); 
       i = scanner.nextInt(); 
      } 
      System.out.println("EXIT"); 
      itemTask.shutdownHook(); 
     } 
    } 


    package ashish.demo.threading.basic; 

    import java.io.Serializable; 

    /** 
    * Created by ashishratan on 2/2/17. 
    */ 
    public class Item implements Serializable { 

     private Integer orderId; 
     private Float price; 
     private String productName; 
     private byte category; 

     public Item(Integer orderId, Float price, String productName, byte category) { 
      this.orderId = orderId; 
      this.price = price; 
      this.productName = productName; 
      this.category = category; 
     } 

     public Item() { 
     } 

     public Integer getOrderId() { 
      return orderId; 
     } 

     public void setOrderId(Integer orderId) { 
      this.orderId = orderId; 
     } 

     public Float getPrice() { 
      return price; 
     } 

     public void setPrice(Float price) { 
      this.price = price; 
     } 

     public String getProductName() { 
      return productName; 
     } 

     public void setProductName(String productName) { 
      this.productName = productName; 
     } 

     public byte getCategory() { 
      return category; 
     } 

     public void setCategory(byte category) { 
      this.category = category; 
     } 

     @Override 
     public String toString() { 
      return "Item{" + 
        "orderId=" + orderId + 
        ", price=" + price + 
        ", productName='" + productName + '\'' + 
        ", category=" + category + 
        '}'; 
     } 


     @Override 
     public boolean equals(Object o) { 
      if (this == o) return true; 
      if (!(o instanceof Item)) return false; 

      Item item = (Item) o; 

      if (getCategory() != item.getCategory()) return false; 
      if (getOrderId() != null ? !getOrderId().equals(item.getOrderId()) : item.getOrderId() != null) return false; 
      if (getPrice() != null ? !getPrice().equals(item.getPrice()) : item.getPrice() != null) return false; 
      return getProductName() != null ? getProductName().equals(item.getProductName()) : item.getProductName() == null; 
     } 

     @Override 
     public int hashCode() { 
      int result = getOrderId() != null ? getOrderId().hashCode() : 0; 
      result = 31 * result + (getPrice() != null ? getPrice().hashCode() : 0); 
      result = 31 * result + (getProductName() != null ? getProductName().hashCode() : 0); 
      result = 31 * result + (int) getCategory(); 
      return result; 
     } 
    } 

Исключение трассировки

Please Enter Number (0) to exit 
    3 
    Thread[main,5,main] 
    Total Items In Stock are 0 
    [] 
    Please Enter Number (0) to exit 
    Thread[ItemTask-Thread-455,10,main] 
    Total Items In Stock are 1 
    6 
    Thread[main,5,main] 
    Total Items In Stock are 1 
    Thread[ItemTask-Thread-464,10,main] 
    Total Items In Stock are 2 
    Exception in thread "main" java.util.ConcurrentModificationException 
     at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:901) 
     at java.util.ArrayList$Itr.next(ArrayList.java:851) 
     at java.util.AbstractCollection.toString(AbstractCollection.java:461) 
     at java.lang.String.valueOf(String.java:2994) 
     at java.io.PrintStream.println(PrintStream.java:821) 
     at ashish.demo.threading.Main.main(Main.java:26) 
    12 
+0

«если (это. ** удалить ** ItemEvent) { this.itemList. ** добавить ** (this.item);" ? – Fildor

+0

Тогда что синхронизируется? –

+0

Какая строка 26 в Main.java? – Fildor

ответ

4

Рекомендации в Java параллелизм P ractice: «Остерегайтесь неявной итерации». У вас есть неявные итерации по линии:

System.out.println(itemTask.getItemList()); 

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

Факт, что синхронизация itemTask.getItemList() не имеет значения - этот монитор удерживается только на время вызова до itemTask.getItemList(): после того, как результат возвращается, монитор больше не удерживается, что означает, что монитор не удерживается когда вы передаете этот результат System.out.println.

Для того, чтобы гарантировать, что у вас есть эксклюзивный доступ к списку вещей в то время как вы печатаете его, явно синхронизирует на itemTask:

synchronized (itemTask) { 
    System.out.println(itemTask.getItemList()); 
} 

Это будет правильно держать монитор для продолжительности System.out.println вызова; модификации в itemTask не могут произойти в одно и то же время, поскольку они имеют место в блоке synchronized (this), где «this» является itemTask, который вы синхронизируете извне.

Альтернативой этому было бы вернуться оборонительную копию списка из метода getItemList:

public synchronized List<Item> getItemList() { 
    this.statusDisplay(); 
    return new ArrayList<>(itemList); 
} 

Это устранило бы необходимость во внешней синхронизации на вызове getItemList, что является более безопасным дизайн, потому что вы не полагаетесь на клиентов своего класса, чтобы «поступать правильно».

+0

Согласны ли вы с тем, что использование метода 'getItemList' в первую очередь не является хорошей идеей? Это приглашение к таким ошибкам. (Предполагая, что метод не возвращает копию). – Fildor

+0

@ Филдор да, я бы хотел. –

+0

не проблема производительности с использованием объекта ItemTask как синхронизированная –

0

Вы печатали список элементов, который включал итерирование списка с помощью Итератора, в то время как в потоке ItemTask произошла структурная модификация (добавление элемента). Итератор считает это плохой, и таким образом остановил мир с исключением. Это называется отказоустойчивым поведением. Вам придется использовать синхронизированный список (что плохо) или сделать блокировку/мьютекс, которые должен получить каждый поток, который хочет изменить или прочитать.

BTW, есть некоторые крайние разногласия, возникающие там, потому что вы использовали целых 500 потоков для обновления состояния одного объекта.

+0

Но я обновляю свой список только в определенном состоянии, и я сделал это изменчивым. когда логическое значение true добавляет иначе. –

+0

@AshishRatan Но нет ничего, что гарантировало бы, что эти две операции не будут проходить одновременно. На самом деле, из-за того, что вы делаете вопиющий спор, это, скорее всего, произойдет даже в нескольких попытках. – glee8e

+0

Хорошо, тогда какое условие будет в безопасности? кроме метода getItemList(), каковы могут быть другие факторы проблемы? –

0

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

Согласно моему пониманию, причина корня: Хотя метод getItemList в ItemTask синхронизирован, он просто возвращает коллекцию в класс Main.Итерация коллекции выполняется в основном методе, который не синхронизирован и в среднем, если другой поток помещает любой элемент в коллекцию, итератор проверяет, есть ли какая-либо модификация в коллекции, и если есть какая-либо модификация, она выдает исключение [очевидно, из трассировки стека в java.util.ArrayList $ Itr.checkForComodification (ArrayList.java:901)].

Если вы переместите печать ItemList в Itemtask в методе getItemList или новый метод синхронизации в Itemtask для печати списка, ошибка не произойдет, по крайней мере, для меня это не произошло.

+0

java.util.ArrayList $ Itr.checkForComodification (ArrayList.java:901)]. резюме вашего ответа. Теперь часть подходит для печати списка. Внутри itemTask я просто хочу понять механизм синхронизации блока, работающего в java. за хороший ответ вы можете пойти с @ Ответ Энди –

 Смежные вопросы

  • Нет связанных вопросов^_^