Я запускаю программу, которая содержит следующие классы (не только, но и те, которые имеют отношение к вопросу)
В классе Results у меня есть синхронизированная LinkedHashMap, например:
private static Map<Integer,Result> resultsHashMap=Collections.synchronizedMap(new LinkedHashMap<Integer, Result>());
и метод получения:
public static Map<Integer,Result> getResultsHashMap() {
return resultsHashMap;
}
Кроме того, внутри моего класса Result есть конструктор с этим синхронизированным кодом:
public Result(){
synchronized (Lock.lock) {
uniqueIdResult++;
}
}
и синхронизированный метод получения как таковой:
public static int getUniqueIdResult() {
synchronized (Lock.lock) {
return uniqueIdResult;
}
}
uniqueIdResult определяется следующим образом:
private static int uniqueIdResult=0;
Также у меня есть класс Lock, состоящий из этого объекта:
public static final Lock lock=new Lock();
Теперь, это важный вопрос, которым я занимаюсь. В моей программе у меня есть следующие две строки, которые создают результат и помещают его в HashMap.
Result result = new Result();
Results.getResultsHashMap().put(Result.getUniqueIdResult(), result);
Я пытаюсь запустить свою программу с разным количеством потоков. Когда он запускается с 1 потоком, вывод такой, как я ожидаю (конкретно, но не обязательно важно, Results.resultsHashMap содержит 433 ключа, что и должно быть, и ключи начинаются с 1).
Но когда я запускаю его с другим количеством потоков, он дает другой результат. Например, работа с 6 потоками каждый раз дает разное количество ключей, иногда 430, иногда 428, иногда 427 и т. д., и начальный ключ не всегда связан с общим количеством ключей (например, total_number_of_keys-starting_key_number+1, который мне показалось в начале какая-то закономерность, но понял что это не так)
Итерация такая:
int counterOfResults=0;
for (Integer key : Results.getResultsHashMap().keySet()) {
System.out.println(key + " " + Results.getResultsHashMap().get(key));
counterOfResults++;
}
System.out.println(counterOfResults);
Кроме того, при синхронизации метода получения для получения hashMap без синхронизации создания Result и вставки в hashMap вывод с несколькими потоками дает неверный вывод.
Кроме того, при синхронизации только одна из строк (создание Result и размещение в hashMap), вывод не является согласованным для нескольких потоков.
Однако, когда я синхронизирую обе эти строки (создание Result и размещение на карте) следующим образом:
Result result;
synchronized (Lock.lock) {
result = new Result(currentLineTimeNationalityNameYearofbirth.getName(),currentLineTimeNationalityNameYearofbirth.getTime(),citycompetionwas,date,distance,stroke,gender,kindofpool);
Results.getResultsHashMap().put(Result.getUniqueIdResult(), result);
}
результат идеален, независимо от того, сколько потоков я использую.
Кроме того, я отмечу, что вывод печатается только после завершения всех потоков с использованием метода join для всех созданных потоков.
Итак, мой вопрос:
Насколько мне известно, перед синхронизацией двух строк (создание Result и помещение в hashMap) все мои критические разделы, например, изменение и получение uniqueIdResult, получение resultsHashMap (как я уже упоминал, я также пытался синхронизировать этот метод получения) синхронизируются с одним и тем же объектом, плюс я добавил еще один безопасный подход при размещении hashMap с Collections.synchronizedMap, что, насколько мне известно, должно сделать hashMap потокобезопасным.
Почему же тогда результат не такой, как я ожидаю? Где проблема с безопасностью?