2015-10-16 2 views
2

У меня есть ArrayList из String s и два потока одновременно обращаются к списку. Что будет выводить следующий фрагмент и почему?Каким будет выход, доступ к списку для разных потоков?

public static void main(String[] args) { 
    final ArrayList<String> list = new ArrayList<String>(); 

    for (int i = 0; i < 100; i++) { 
     list.add("Number" + i); 
    } 

    new Thread() { 

     public void run() { 
      for (String s : list) { 
       System.out.println(s); 
      } 
     } 
    }.start(); 

    new Thread() { 
     public void run() { 
      list.remove("Number5"); 
     } 
    }.start(); 

} 

Я попытался использовать тот же код, что делает Arraylist синхронизированы с помощью Collections.synchronizedList(list). Он все еще бросает java.util.ConcurrentModificationException.

+0

Вы хотите спросить: «Является ли эта функция безопасной?»? –

ответ

3

Javadoc из synchronizedList четко говорится, что:

Крайне важно, чтобы пользователь вручную синхронизировать на возвращаемом списке при переборе над ним:

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

Несоблюдение этой рекомендации может привести к в недетерминированном поведении.

Поскольку в этом коде вы удаляете из List в то время как вы итерацию над ним (в другом потоке, но это тот же самый экземпляр List), ConcurrentModificationException будет отброшена.

Используя Collections.synchronizedList, следующий код будет работать нормально.

final List<String> list = Collections.synchronizedList(new ArrayList<String>()); 
for (int i = 0; i < 100; i++) { 
    list.add("Number" + i); 
} 

new Thread() { 
    public void run() { 
     synchronized (list) { 
      for (String s : list) { 
       System.out.println(s); 
      } 
     } 
    } 
}.start(); 

new Thread() { 
    public void run() { 
     synchronized (list) { 
      list.remove("Number5"); 
     } 
    } 
}.start(); 
1

Вы iterating (в первом потоке) и updating (во втором потоке) то же list объекта в двух различных потоков, так что всегда есть (возможность), что он будет бросать java.util.ConcurrentModificationException.

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

Если вы хотите использовать тот же объект списка, который вам нужен, вы можете его синхронизировать, используя synchronized(list) как в потоках run.

public void run() { 
     synchronized(list) { 
      for (String s : list) { 
       System.out.println(s); 
      } 
     } 
    } 

    public void run() { 
     synchronized(list) { 
      list.remove("Number5"); 
     } 
    } 
0

Я думаю, вы должны использовать Vector вместо ArrayList для того ThreadSafe плюс используя Iterator для просмотра списка.

2

Вы можете пометить замок на объекте list, используя ключевое слово syncrhonized.

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

Так это должно работать для вас:

public static void main(String[] args) { 
    ... 

    new Thread() { 

     public void run() { 
     synchronized(list){ 
      for (String s : list) { 
       System.out.println(s); 
      } 
     } 
     } 
    }.start(); 

    new Thread() { 
     public void run() { 
     synchronized(list){ 
        list.remove("Number5"); 
     } 
     } 
    }.start(); 

} 

NB: Это зависит от того, какой логики вы хотите кстати. Вы хотите удалить из списка, как вы его перебираете или? вы хотите, чтобы две задачи выполнялись последовательно?

+1

меня тоже: искал цель. Если потребуется, я думаю, нам придется чередовать задачи между двумя потоками. Более того, добавление и удаление похоже на проблему производителя-потребителя (для печати всего списка требуется блокировка всего объекта списка, в зависимости от намерения разработчика). –

+0

Да, все зависит от намерения разработчика. Но разработчик не комментирует так .. :-) – dsharew