synchronized(count) — блокировка на кэшированном объекте

JVM кэширует Integer в диапазоне от -128 до 127 через Integer.valueOf() — это тот же механизм, что даёт true для Integer a = 100; Integer b = 100; a == b. Проблема начинается, когда такой закэшированный объект используют как объект блокировки.

Если synchronized берёт в качестве монитора Integer со значением из кэшируемого диапазона, любой другой код в приложении, который тоже синхронизируется на Integer с тем же значением, получает тот же монитор — просто потому что оба Integer.valueOf(5) возвращают одну и ту же ссылку.

Два совершенно не связанных участка кода начинают блокировать друг друга, и это не видно по чтению кода — оба выглядят так, будто у каждого свой приватный лок.

private final Integer lock = count; // count = 5

public void process() { synchronized (lock) { // критическая секция } }

Спросят следом: почему для лока нужен именно new Object(), а не любой существующий объект. Ответ — new Object() гарантированно создаёт уникальный монитор, который не пересекается ни с чьим кэшем и ни с чьей идентичностью.

Объект для synchronized должен быть выделенным полем, созданным именно под эту блокировку — не значением, которое JVM может закэшировать или разделить.

Тренажёр: 600 вопросов, мок с таймером, план повторов

senior·base — что спрашивают на самом деле


В этом посте были ссылки, но мы их удалили по правилам Сетки