Изменить внешний объект из потока
public void fooAndBar() {
HashMap<Foo, Bar> fooBarMap = new HashMap<>();
CompletionService completionService = new ExecutorCompletionService(exec);
for(int i=0; i<10; i++) {
completionService.submit(new Callable() {
@Override
public Void call() throws Exception {
fooBarMap.put(new Foo(i), new Bar(i));
return null;
}
});
}
}
Безопасно ли изменять HashMap внутри
Callable
?Если хэш-карта должна быть
final
(или, может бытьvolatile
) и если да, то почему?Должен ли я использовать структуру, кроме
HashMap
, что-то вродеConcurrentHashMap
или жеSynchronizedMap
и почему?
Я пытаюсь понять концепции Java, поэтому, пожалуйста, потерпите меня
2 ответа
Безопасно ли изменять HashMap внутри Callable?
Нет. Если вы используете пул потоков, я предполагаю, что вы планируете, чтобы больше этих вызываемых элементов выполнялось параллельно. Каждый раз, когда к объекту с изменяемым состоянием обращаются из более чем одного потока, это небезопасно для потока. Если вы пишете в небезопасный хеш-файл из двух потоков одновременно, его внутренняя структура будет повреждена. Если вы читаете из небезопасного hashmap, в то время как другой поток пишет в него одновременно, ваш поток чтения будет читать мусор. Это очень хорошо известная и широко изученная ситуация, известная как "состояние гонки", описание которой будет полностью выходить за рамки этого ответа. Для получения дополнительной информации читайте о состоянии гонки в Википедии или о другом вопросе, на который был дан ответ в 2008 году: Stackru - Что такое состояние гонки.
Должна ли хеш-карта быть окончательной (или, возможно, изменчивой) и если да, то почему?
Для ваших целей он не обязательно должен быть окончательным, но всегда полезно делать окончательное все, что можно сделать окончательным.
Это не должно быть изменчивым, потому что:
если бы вы сделали его изменчивым, вы бы сделали ссылку на него изменчивой, но ссылка никогда не изменится, меняется только ее содержимое, а переменная не имеет к этому никакого отношения.
поток потоков гарантирует, что
call()
будет выполнен послеfooBarMap = new HashMap<>()
, (Если вам интересно, почему такая вещь может вызывать беспокойство, поищите в Google "границу памяти".)
Должен ли я использовать структуру, отличную от HashMap, что-то вроде ConcurrentHashMap или SynchronizedMap и почему?
Определенно. Потому что, как я писал ранее, каждый раз, когда объект с изменяемым состоянием доступен из более чем одного потока, это небезопасно для потока. А также ConcurrentHashMap
, SynchronizedMap
, synchronize
и т. д. существуют именно для того, чтобы позаботиться о небезопасных ситуациях.
Hashmap не должен быть окончательным, так как вы изменяете его несколько раз (из цикла for).
Если вы сделаете это окончательно, вы можете получить ошибку.