У меня есть часть кода, которая может быть выполнена несколькими потоками, которые должны выполнить операцию привязки ввода-вывода, чтобы инициализировать общий ресурс, который хранится в ConcurrentMap
. Мне нужно сделать этот поток кода безопасным и избежать ненужных вызовов для инициализации общего ресурса. Вот код ошибки:
private ConcurrentMap<String, Resource> map;
// .....
String key = "somekey";
Resource resource;
if (map.containsKey(key)) {
resource = map.get(key);
} else {
resource = getResource(key); // I/O-bound, expensive operation
map.put(key, resource);
}
В приведенном выше коде несколько потоков могут проверять ConcurrentMap
и видеть, что ресурса нет, и все пытаются вызвать getResource()
, что дорого. Чтобы обеспечить только одну инициализацию общего ресурса и сделать код эффективным после инициализации ресурса, я хочу сделать что-то вроде этого:
String key = "somekey";
Resource resource;
if (!map.containsKey(key)) {
synchronized (map) {
if (!map.containsKey(key)) {
resource = getResource(key);
map.put(key, resource);
}
}
}
Это безопасная версия двойной проверки блокировки? Мне кажется, что, поскольку проверки вызывают на ConcurrentMap
, он ведет себя как общий ресурс, объявленный как volatile
и, таким образом, предотвращает любую из проблем "частичной инициализации", которые могут произойти.