В качестве альтернативы,
StringBuilder result = new StringBuilder();
for(String string : collection) {
result.append(string);
result.append(',');
}
return result.substring(0, result.length() - 1) ;
Вы используете хорошо известный класс, не поддерживающий потоки, и жалуетесь на взаимоблокировку. Я не понимаю, в чем проблема.
Кроме того, как 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
никогда не возвращается, и программа зависает. Но это не тупик и не лайвлок, а состояние гонки .
С точки зрения выполнения теста - вместо:
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...
Что вы понимаете под тупиком?
В коде есть как минимум две проблемы. HashMap
используется из нескольких потоков одновременно и поэтому может попасть в бесконечный цикл. Вы повторяете набор записей, потенциально изменяя базовую структуру данных (даже если каждая отдельная операция была синхронизирована hasNext
/ next
не будет атомарной).
Также обратите внимание, что Версии 1.6.0 до последней версии Synhronized Security Release (SSR) - 1.6.0_13 и 1.6.0_14.
Я считаю, что ваша карта модифицируется одновременно. Если put () вызывается во время выполнения вашего итерационного действия, при определенных условиях (особенно если происходит изменение размера) вы можете попасть в бесконечный цикл. Это довольно хорошо известное поведение (см. здесь ).
Тупик и бесконечный цикл проявляются по-разному. Если у вас настоящий тупик, дамп потока четко покажет блокирующие потоки. С другой стороны, как только вы попадаете в бесконечный цикл, ваш процессор будет резко повышаться, и трассировки стека будут меняться каждый раз, когда вы делаете дамп.
Это не имеет ничего общего с Executor и все, что связано с unsafe одновременное использование HashMap , который никогда не предназначался для такого использования. Фактически, довольно легко воспроизвести эту проблему с помощью массива нескольких потоков.
Лучшее решение этого - переключиться на ConcurrentHashMap. Если вы переключитесь на синхронизированную HashMap или Hashtable, вы не попадете в бесконечный цикл, но все равно можете получить исключения ConcurrentModificationExceptions во время итерации.