Параллелизм Java: Volatile vs final в" Каскадных " переменных?

is

final Map<Integer,Map<String,Integer>> status = new ConcurrentHashMap<Integer, Map<String,Integer>>();
Map<Integer,Map<String,Integer>> statusInner = new ConcurrentHashMap<Integer, Map<String,Integer>>();
status.put(key,statusInner);

аналогично

volatile Map<Integer,Map<String,Integer>> status = new ConcurrentHashMap<Integer,   Map<String,Integer>>();
Map<Integer,Map<String,Integer>> statusInner = new ConcurrentHashMap<Integer, Map<String,Integer>>();
status.put(key,statusInner);

в случае, если внутренняя карта доступна разными потоками?

или даже что-то вроде этого требуется:

volatile Map<Integer,Map<String,Integer>> status = new ConcurrentHashMap<Integer, Map<String,Integer>>();
volatile Map<Integer,Map<String,Integer>> statusInner = new ConcurrentHashMap<Integer, Map<String,Integer>>();
status.put(key,statusInner);

в случае, если это не "каскадная" карта, final и volatile в конечном итоге имеют тот же эффект, что и shure, что все потоки всегда видят правильное содержимое карты... Но что произойдет, если карта iteself содержит карту, как в Примере... Как мне сделать так, чтобы внутренняя карта правильно "ограждена памятью"?

танки! Тома

3 ответов


volatile влияет только на способность других потоков считывать значение переменных, к которым он прикреплен. Это никоим образом не влияет на способность другого потока, чтобы просмотреть ключи и Значения карты. Например, я мог бы иметь volatile int[]. Если я изменю ссылку, то есть если я изменю фактический массив, на который он указывает, другие потоки, считывающие массив, гарантированно увидят это изменение. Однако, если я изменю третий элемент массива, такие гарантии не будут сделаны.

если status и final, конструкция содержащего класса создает happens-before отношение с любыми последующими чтениями, поэтому они могут видеть значение статуса. Аналогично любой читает ваш volatile переменная гарантированно увидит последнее назначение ссылки на нее. Это не похоже на то, что вы очень часто меняете фактическую карту, больше похоже на то, что вы просто меняете ключи, и общий объект карты остается как есть.

на этот вопрос, мы должны обратиться к документации по ConcurrentHashMap:

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

это немного странно сформулировано, но суть в том, что любой get операция, начало которой после некоторые put возвращение деятельности гарантировано для того чтобы увидеть результаты что положено. Так что вам даже не нужно volatile на внешней карте; quoth JLS:

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

резюме

A final на внешней карте достаточно.


стоит Google-Коллекции и, в частности, картографа это позволяет разумно настраивать и создавать карты. Возможность настройки слабых значений, улучшения сбора мусора и времени истечения срока действия, чтобы вы могли использовать карты для эффективного кэширования, является блестящей. Поскольку карты, которые делает MapMaker (: p), имеют те же свойства, что и ConcurrentHashMap, вы можете быть довольны его потокобезопасностью.

final mapMaker = new MapMaker().weakValues(); //for convenience, assign
final Map<Integer,Map<String,Integer>> status = mapMaker.makeMap();
status.put(key, mapMaker.<String, Integer>makeMap());

обратите внимание, вы, возможно, захотите посмотрите на свое определение statusInner, как это не кажется правильным.


Я думаю, что лучший ответ здесь-это volatile не является способом обеспечения потокобезопасности.

используя ConcurrentHashMap почти все, что вам нужно. Да, сделайте ссылку на верхний уровень Map final Если вы можете, но volatile Не надо в любом случае. Второй уровень Map ссылка внутри-это бизнес ConcurrentHashMap чтобы получить право, и предполагается, что это так.