Почему ExecutorService заходит в тупик при выполнении операций HashMap?

В качестве альтернативы,

StringBuilder result = new StringBuilder();
for(String string : collection) {
    result.append(string);
    result.append(',');
}
return result.substring(0, result.length() - 1) ;
5
задан Stephen 1 July 2009 в 09:48
поделиться

4 ответа

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

Кроме того, как ExecutionService

strangely lacking

?

Распространенное заблуждение состоит в том, что при использовании , например , HashMap ] вы получите в лучшем случае устаревшие данные. См. красивое состояние гонки о том, как вы можете взорвать свою JVM, сделав именно это.

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

Что касается ConcurrentHashMap, просто прочтите javadoc - он должен прояснить ваши вопросы. Если нет, взгляните на Java Concurrency in Practice .


Обновление:

Мне удалось воспроизвести вашу ситуацию, но это не тупик. Одно из действий никогда не завершается. Трассировка стека выглядит так:

"pool-1-thread-3" prio=10 tid=0x08110000 nid=0x22f8 runnable [0x805b0000]
java.lang.Thread.State: RUNNABLE
at ExecutorTest$3.call(ExecutorTest.java:36)
at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303)
at java.util.concurrent.FutureTask.run(FutureTask.java:138)
at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
 at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
at java.lang.Thread.run(Thread.java:619)

Похоже, что точный случай, с которым я связался, - HashMap изменяется, и из-за внутренней механики изменения размера итератор застревает в бесконечном цикле.

Когда это происходит, invokeAll никогда не возвращается, и программа зависает. Но это не тупик и не лайвлок, а состояние гонки .

16
ответ дан 18 December 2019 в 08:30
поделиться

С точки зрения выполнения теста - вместо:

 executor.invokeAll(actions);

используйте также

 executor.invokeAll(actions, 2, TimeUnit.SECONDS);

обратите внимание, что для того, чтобы тест действительно работал (и сообщал об ошибках), вам нужно сделать что-то вроде:

 List<Future> results = executor.invokeAll(actions, 2, TimeUnit.SECONDS);
 executor.shutdown();
 for (Future result : results) {
     result.get(); // This will report the exceptions encountered when executing the action ... the ConcurrentModificationException I wanted in this case (or CancellationException in the case of a time out)
 }
 //If we get here, the test is successful... 
0
ответ дан 18 December 2019 в 08:30
поделиться

Что вы понимаете под тупиком?

В коде есть как минимум две проблемы. HashMap используется из нескольких потоков одновременно и поэтому может попасть в бесконечный цикл. Вы повторяете набор записей, потенциально изменяя базовую структуру данных (даже если каждая отдельная операция была синхронизирована hasNext / next не будет атомарной).

Также обратите внимание, что Версии 1.6.0 до последней версии Synhronized Security Release (SSR) - 1.6.0_13 и 1.6.0_14.

2
ответ дан 18 December 2019 в 08:30
поделиться

Я считаю, что ваша карта модифицируется одновременно. Если put () вызывается во время выполнения вашего итерационного действия, при определенных условиях (особенно если происходит изменение размера) вы можете попасть в бесконечный цикл. Это довольно хорошо известное поведение (см. здесь ).

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

Это не имеет ничего общего с Executor и все, что связано с unsafe одновременное использование HashMap , который никогда не предназначался для такого использования. Фактически, довольно легко воспроизвести эту проблему с помощью массива нескольких потоков.

Лучшее решение этого - переключиться на ConcurrentHashMap. Если вы переключитесь на синхронизированную HashMap или Hashtable, вы не попадете в бесконечный цикл, но все равно можете получить исключения ConcurrentModificationExceptions во время итерации.

1
ответ дан 18 December 2019 в 08:30
поделиться
Другие вопросы по тегам:

Похожие вопросы: