AtomicXXXFieldUpdater
— чудесные классы, открывающие значительную часть магии
Unsafe
без проблем с безопасностью и переносимостью. К сожалению, есть некоторая цена. Вот цитата из
AtomicLongFieldUpdater.CASUpdater
:
public void lazySet(T obj, long newValue) {
if (obj == null || obj.getClass() != tclass || cclass != null)
fullCheck(obj);
unsafe.putOrderedLong(obj, offset, newValue);
}
private void ensureProtectedAccess(T obj) {
if (cclass.isInstance(obj))
return;
throw new RuntimeException (
new IllegalAccessException("Class " + cclass.getName() +
" can not access a protected member of class " + tclass.getName() +
" using an instance of " + obj.getClass().getName()
)
);
}
private void fullCheck(T obj) {
if (!tclass.isInstance(obj))
throw new ClassCastException();
if (cclass != null)
ensureProtectedAccess(obj);
}
Выглядит все довольно страшно, хотя на практике в большинстве случаев все закончится на первых трех проверках: (
obj == null || obj.getClass() != tclass || cclass != null
). Эффект от них невелик, в моих бенчмарках замена
AtomicUpdater
на
Unsafe
давала 15-20% максимум в очень нагруженном конкурентном коде. По-видимому, дело в том, что результат этих проверок в правильно написанном коде всегда одинаков (false), поэтому предсказатель ветвлений в процессоре довольно быстро сообразит, какая из веток реализуется со 100% вероятностью, и будет спекулятивно выполнять код дальше. Тем не менее, иногда не хочется терять и этих процентов.
Хорошая новость состоит в том, что
AtomicLongFieldUpdater
— абстрактный класс, и никто не мешает вам написать свою реализацию. Например, взять за основу
AtomicLongFieldUpdater.CASUpdater
, и просто выбросить все "лишние" проверки. Результат будет практически drop-in-replacement для большинства сценариев использования.
Разумеется, у меня сразу возникла мысль сделать какую-то свою фабрику, на замену
AtomicLongFieldUpdater.newUpdater(...)
, которая по-умолчанию делегировала бы к
AtomicLongFieldUpdater.newUpdater
, а при указании специального свойства
-Djava.concurrent.run-faster
начинала бы использовать оптимизированную версию. Плохая новость состоит в том, что так не получается: в конструкторе
AtomicFieldUpdater
-а проверяется, что вы можете создать
AtomicUpdater
только для того поля, которое вы и так можете видеть из создающего кода. Если я собираюсь обновлять private поле, то
AtomicUpdater
для него я смогу создать только
изнутри этого же класса. И значит выбор реализации
AtomicUpdater
-а придется делать внутри каждого класса, его использующего.