2015-10-17 6 views
2

Учитывая ConcurrentHashMap JavaDocs состояние:ConcurrentHashMap итерация Гарантийное

«итераторы и Перечисления возвращают элементы, отражающие состояние хэш-таблицы в какой-то точке или с момента создания итератора»

Я думаю, что я гарантирован, что в приведенном ниже примере один или оба потока вызовут fireAllFinished(). Может ли когда-нибудь быть случай, когда ни один из вызовов fireAllFinished()?

ConcurrentHashMap<String, Boolean> taskToFinished = new ConcurrentHashMap(); 
    taskToFinished.put("taskA", false); 
    taskToFinished.put("taskB", false); 

public void checkForAllFinished() { 
    boolean allFinished = true; 
    for (Boolean taskFinished = tasksToFinished.values()) { 
     if (!taskFinished) { 
      allFinished = false; 
      break; 
     } 
    } 
    if (allFinished) { 
     fireAllFinished() 
    } 
} 

//Thread1 
public void run() { 
    taskToFinished.put("taskA", true); 
    checkForAllFinished(); 
} 

//Thread1 
public void run() { 
    taskToFinished.put("taskB", true); 
    checkForAllFinished(); 
} 

(я пропустил часть кода создания потоков, я надеюсь, что цель ясна.)

обновление: Я уже видел это более общий вопрос: Is iterating ConcurrentHashMap values thread safe?, но хотел, чтобы подтвердить свой специфический точка, как

"в какой-то момент"

, как правило, имп что при работе с многоядерными машинами, работающими с кодом не по порядку, два потока могут одновременно обновлять разные сегменты карты, и нет никакого способа заблокировать весь ConcurrentHashMap.

+0

Эта программа не работает. Метод 'checkForAllFinished' имеет локальную переменную' allFinished', которая является локальной. Локальные переменные всегда являются потокобезопасными, потому что они находятся в стеке и никогда не могут быть замечены другими потоками. По этой причине ваш метод 'checkForAllFinished' не будет работать так, как вы ожидаете. Если вы создадите поле экземпляра 'allFinished', оно должно быть либо синхронизировано, либо сделано' volatile', чтобы предотвратить устаревшие данные. – scottb

+0

@scottb, что вы сказали, неверно. – Jason

+0

Если вы ожидаете завершения набора задач, почему вы не используете 'Future'? – Jason

ответ

2

Чтение документации для ConcurrentHashMap ...

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

и

Для агрегированных операций такие как putAll и четкие параллельные поисковые запросы, могут отражать вставку или удаление только некоторых записей. Аналогично, итераторы, разделители и перечисления возвращают элементы, отражающие состояние хеш-таблицы в какой-то момент времени или после создания итератора/перечисления.

Это не сформулировано четко, но что совсем недавно завершила и на или со должны иметь в виду, что операции на карте и создания итератора, последовательно последовательны.

Используя ваш пример, если мы называем карту ставить и контрольное значение B у вас есть ...

Т1: ->Б

Т2: ->Б

происходит до B, но T1 и T2 происходят одновременно. Какими последовательно согласованными средствами является некоторая действительная последовательность между двумя должно быть до тех пор, пока A происходит до B. Тем не менее, выполняется любое упорядочение между T1 и T2.

например.

Т1: а ->Т1: В ->Т2: а ->Т2: В

Т1: а ->Т2: а ->Т2 : B ->T1: B

Так что, когда код на самом деле работает, любое допустимое упорядочение может произойти, но, T1: B или T2: B (проверка) должна быть последней. Итак, fireAllFinished вызывается один или два раза. Линейная синхронизация ограничивала бы еще более четкое упорядочение между всеми событиями.

Итерация по всей карте может быть немного дорогостоящей, хотя и может быть проще просто использовать AtomicInteger или другой механизм синхронизации, такой как ConcurrentLinkedQueue.

+0

Я согласен с Джейсоном. Итератор гарантирует моментальный снимок карты для всех завершенных операций и НЕ ГАРАНТИРУЕТ, что вы увидите более поздние изменения в пределах одного итератора.Поэтому, чтобы ответить на ваш пример, возможно, что ваша программа не вызовет метод fireAllFinished для обоих потоков, потому что один из них мог обновить карту после создания итератора. – gmconte

+0

Меня беспокоит только то, что fireAllFinished получает нулевое время. Если это называется один или два раза, это нормально. – barclar

+0

Я думаю, что линейно синхронизированный означает, что никогда не будет случая, когда ни один поток не вызывает fireAllFinished(). – barclar