Этот код небезопасен по двум причинам, обе из которых основаны на следующих утверждениях:
if (list == null) {
list = new ArrayList<>();
}
return list;
Первая проблема - это состояние гонки. В системе с несколькими ядрами существует небольшая вероятность того, что два потока будут читать list
в одно и то же время, оба потока увидят null
, и два объекта ArrayList
будут созданы и возвращены. А в одноядерной системе существует еще меньшая вероятность того, что один поток будет заменен другим сразу после чтения list
, и вы получите тот же результат.
Но есть более коварная проблема, связанная с моделью памяти Java. Тот факт, что синхронизация отсутствует, означает, что между 1011 * записывающим list
и другим (впоследствии) считывающим его потоком не существует отношения произойдет до . Это означает, что интерпретатор Java / JIT-компилятор не обязаны вставлять барьер памяти последовательности, чтобы гарантировать, что значение, записанное более ранним потоком, будет видимым для более позднего потока. Так что даже в случае, когда два потока работают в разное время ... более поздний поток может не видеть ненулевое значение, записанное более ранним потоком из-за различных эффектов кэширования памяти.
Решением обеих проблем является правильная синхронизация; например как это:
synchronized (this) {
if (list == null) {
list = new ArrayList<>();
}
return list;
}
или объявив метод равным synchronized
.