Протестируйте слабую ссылку перед ее использованием java

В многопоточном проекте Android я вижу такой код:

final WeakReference<MyClass> myClassObjectWeakRef =
                new WeakReference<MyClass>(aMyClassObject);

... затем в другом месте:

if (myClassObjectWeakRef.get() != null) {
  myClassObjectWeakRef.get().someMethod();
}

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

Я бы подумал, что единственный правильный способ проверить и использовать слабую ссылку:

MyClass myObject = myClassObjectWeakRef.get();
// we now have a strong reference, or null: standard checks apply.
if (myObject != null) {
  myObject.someMethod();
}

Я очень уверен, что второй метод на 100% безопасен, но мне интересно, есть ли какой-то Java/компилятор/магия, о котором я не знаю, что сделало бы первый метод безопасным.

Итак, первый метод на 100% безопасен или нет?

Ответы

Ответ 1

Первый метод определенно небезопасен. Каждый вызов get является независимым. Ничего не мешает GC очистить слабодоступный объект после первого get и до второго.

javadoc утверждает

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

Это может быть в любой момент времени. Вызов get(), который (потенциально) толкает ссылку на объект в стеке, временно делает объект сильно доступным (он в стеке потоков), но эта способность досягаемости исчезает, когда заканчивается сравнение с null. После этого момента GC может определить, что объект слабо доступен, и очистить его ссылку. Затем вы получите NullPointerException.

Используйте второй метод. Но обратите внимание, что, назначив его переменной, вы делаете ссылочный объект сильно доступным.