2016-03-23 5 views
0

Я пытался попробовать проблему производителя и потребителя с помощью ArrayList (я знаю, что Arraylist isnt threadsafe), и я убедился, что я поставил список с ключевым словом synchronized, но все еще попал в ConcurrentModificationException. Это ошибкаArrayList не является потокобезопасным даже с синхронизированным блоком?

Начиная Производитель Просьба предоставить сведения о задании: TestJob Работа завершена: TestJob Исключение в потоке "потребитель" java.util.ConcurrentModificationException в java.util.ArrayList $ Itr.checkForComodification (Unknown Source) в java.util.ArrayList $ Itr.next (Unknown Source) в test.thread.Consumer.run (Consumer.java:25) в java.lang.Thread.run (Unknown Source)

Это мой код:

package test.thread; 

import java.util.List; 
import java.util.Scanner; 

public class Producer implements Runnable { 

    private List<String> jobs = null; 

    public Producer(List<String> jobs) { 
     this.jobs = jobs; 
    } 

    @Override 
    public void run() { 

     System.out.println("Starting Producer"); 
     while(true) 
     { 
      synchronized (jobs) { 
       try { 
       if (jobs.isEmpty()) { 


         System.out.println("Please provide the job details: "); 
         Scanner scanner = new Scanner(System.in); 
         String job = scanner.nextLine(); 
         jobs.add(job); 
         scanner.close(); 
         jobs.notifyAll(); 
         Thread.sleep(4000); 

       } else { 
        jobs.wait(); 
       } 
       } catch (InterruptedException e) { 
        e.printStackTrace(); 
       } 

      } 
     } 

    } 

} 


package test.thread; 

import java.util.List; 

public class Consumer implements Runnable { 

    private List<String> jobs = null; 

    public Consumer(List<String> list) { 
     this.jobs = list; 
    } 

    @Override 
    public void run() { 

     while(true) 
     { 
      synchronized (jobs) { 
       try { 
        if (jobs.isEmpty()) { 

         jobs.wait(); 

        } else { 
         for (String job : jobs) { 
          System.out.println("Job completed: " + job); 
          jobs.remove(job); 
          Thread.sleep(2000); 
         } 

         jobs.notifyAll(); 
        } 
       } catch (InterruptedException e) { 
        e.printStackTrace(); 
       } 

      } 

     } 

    } 

} 



package test.thread; 

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

public class TestThread { 

    public static void main(String...strings) 
    { 

     List<String> list = new ArrayList<String>(); 
     Producer producer = new Producer(list); 
     Consumer consumer = new Consumer(list); 
     Thread prodThread = new Thread(producer, "producer"); 
     Thread consThread = new Thread(consumer, "consumer"); 
     prodThread.start(); 
     consThread.start(); 
    } 




} 
+2

* Все * обращения к ArrayList должны быть синхронизированы в одном экземпляре. Это не так в вашем коде (например, геттеры и сеттеры). –

+0

Не могли бы вы рассказать? – Rupesh

+0

@ AndyTurner Ах, убрал мой комментарий. –

ответ

3

Хотя вы получаете ConcurrentModificationException ваша проблема не вызвана многопоточности вообще.

На самом деле это проблематичен код:

for (String job : jobs) { 
    System.out.println("Job completed: " + job); 
    jobs.remove(job); 
    Thread.sleep(2000); 
} 

Цикл for будет использовать Iterator над рабочими местами, и в то же время вы непосредственно (структурно) модифицировать jobs (т.е. не через Iterator). Это означает, что Iterator уже не является корректным, и это исключает, чтобы рассказать вам об этом.

Если вам необходимо удалить элементы из коллекции в то время как вы итерацию над ним, вам нужно сделать Iterator явным:

Iterator<String> jobIterator = job.iterator(); 
while (jobIterator.hasNext()) { 
    String job = jobIterator.next(); 
    // ... stuff ... 
    jobIterator.remove(); // remove the last object that was returned by next() 
} 
3

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

Если вы хотите удалить элементы, необходим явный итератор, на который звонить удалить:

Iterator<String> it = jobs.iterator(); 
while (it.hasNext()) { 
    String job = it.next(); 
    System.out.println("Job completed: " + job); 
    it.remove(); 
    Thread.sleep(2000); 
} 

Конечно, удаление элементов из ArrayList, как это очень неэффективно (это O(n^2)), и ошибка -простой, поскольку вы должны явно синхронизировать. Вы должны посмотреть на использование своего рода BlockingQueue.

1

Вы ConcurrentModificationException не из-за параллельности: вы просто удалить элемент в цикле:

for (String job : jobs) { 
    System.out.println("Job completed: " + job); 
    jobs.remove(job); 

Использование итератора для удаления элементов.

2

Ваша одновременная модификация не вызвана резьбой.

for (String job : jobs) { 
         System.out.println("Job completed: " + job); 
         jobs.remove(job); 
         Thread.sleep(2000); 
        } 

Линия jobs.remove(job); изменяет ArrayList в то время как вы итерацию над ним. Итераторы разрешают только модификацию списка, когда это делается с использованием метода удаления Iterators для этого.

 for(Iterator<String> iter = jobs.iterator(); iter.hasNext();) 
    { 
      String job = iter.next(); 
      iter.remove(); 
    } 
0

Вы должны использовать CopyOwWriteArrayList для того, чтобы предотвратить ConcurrentModificationException при использовании списка Iterator (для каждого цикла в вашем коде используется его под капотом)

0

Невозможно удалить элементы во время использования, пожалуйста, используйте метод итератора для удаления.

Iterator<String> it = jobs.iterator(); 
while(it.hasNext()) { 
     String job = it.next(); 
     .... 
     it.remove(); 
     ... 
} 

Это позволит избежать одновременного исключения.

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