Класс MutableCallSite
- java.lang.Object
-
- java.lang.invoke.CallSite
-
- java.lang.invoke.MutableCallSite
public class MutableCallSite extends CallSite
Объект MutableCallSite — это CallSite, целевая переменная которого ведет себя как обычное поле. Инструкция invokedynamic, связанная с MutableCallSite, делегирует все вызовы текущей цели сайта. Динамический вызывающий объект (CallSite) изменяемого вызова сайта также делегирует каждый вызов текущей цели сайта.
Вот пример изменяемого вызова сайта, который вводит переменную состояния в цепочку обработчиков методов.
MutableCallSite name = new MutableCallSite(MethodType.methodType(String.class));
MethodHandle MH_name = name.dynamicInvoker();
MethodType MT_str1 = MethodType.methodType(String.class);
MethodHandle MH_upcase = MethodHandles.lookup()
.findVirtual(String.class, "toUpperCase", MT_str1);
MethodHandle worker1 = MethodHandles.filterReturnValue(MH_name, MH_upcase);
name.setTarget(MethodHandles.constant(String.class, "Rocky"));
assertEquals("ROCKY", (String) worker1.invokeExact());
name.setTarget(MethodHandles.constant(String.class, "Fred"));
assertEquals("FRED", (String) worker1.invokeExact());
// (mutation can be continued indefinitely) Один и тот же вызов сайта может использоваться в нескольких местах одновременно.
MethodType MT_str2 = MethodType.methodType(String.class, String.class);
MethodHandle MH_cat = lookup().findVirtual(String.class,
"concat", methodType(String.class, String.class));
MethodHandle MH_dear = MethodHandles.insertArguments(MH_cat, 1, ", dear?");
MethodHandle worker2 = MethodHandles.filterReturnValue(MH_name, MH_dear);
assertEquals("Fred, dear?", (String) worker2.invokeExact());
name.setTarget(MethodHandles.constant(String.class, "Wilma"));
assertEquals("WILMA", (String) worker1.invokeExact());
assertEquals("Wilma, dear?", (String) worker2.invokeExact()); Несинхронизация значений целевых переменных: Запись в целевую переменную изменяемого вызова сайта не заставляет другие потоки осознать обновленное значение. Потоки, которые не выполняют соответствующие синхронизирующие действия относительно обновленного вызова сайта, могут кэшировать старое значение целевой переменной и неопределенно откладывать использование нового значения целевой переменной. (Это обычное следствие модели памяти Java, применяемой к полям объектов.)
Операция syncAll предоставляет способ принудительно заставить потоки принять новое значение целевой переменной, даже если нет другой синхронизации.
Для целевых значений, которые будут часто обновляться, рекомендуется использовать сайт с volatile-переменной вместо этого.
Конструкторы
| Конструктор и описание |
|---|
MutableCallSite(MethodHandle target) Создает объект вызова сайта с начальным целевым методом. |
MutableCallSite(MethodType type) Создает пустой объект вызова сайта с заданным типом метода. |
Методы
| Модификатор и тип | Метод и описание |
|---|---|
MethodHandle |
dynamicInvoker() Возвращает обработчик методов, эквивалентный инструкции invokedynamic, которая была связана с этим вызовом сайта. |
MethodHandle |
getTarget() Возвращает целевой метод вызова сайта, который ведет себя как обычное поле объекта |
void |
setTarget(MethodHandle newTarget) Обновляет целевой метод этого вызова сайта как обычную переменную. |
static void |
syncAll(MutableCallSite[] sites) Выполняет операцию синхронизации для каждого вызова сайта в заданном массиве, заставляя все другие потоки выбросить любые кэшированные значения, ранее загруженные из целевой переменной любого из вызовов сайта. |
Методы, унаследованные от класса java.lang.invoke.CallSite
type Методы, унаследованные от класса java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait Конструкторы
MutableCallSite
public MutableCallSite(MethodType type)
Создаёт пустой объект вызова с заданным типом метода. Начальный целевой метод устанавливается в метод-обработчик заданного типа, вызов которого приведёт к исключению IllegalStateException.
Тип вызова устанавливается постоянно, в соответствии с заданным типом.
Перед тем, как этот CallSite объект будет возвращён методом инициализации или вызван каким-либо другим образом, ему обычно присваивается более полезный целевой метод с помощью вызова setTarget.
- Параметры:
-
type— тип метода, который будет иметь этот объект вызова - Исключения:
-
NullPointerException— если предложенный тип равен null
MutableCallSite
public MutableCallSite(MethodHandle target)
Создаёт объект вызова с начальным целевым методом-обработчиком. Тип объекта вызова постоянно устанавливается в тип начального целевого метода.
- Параметры:
-
target— метод-обработчик, который будет начальным целевым методом объекта вызова - Исключения:
-
NullPointerException— если предложенный целевой метод равен null
Методы
getTarget
public final MethodHandle getTarget()
Возвращает целевой метод объекта вызова, который ведет себя как обычное поле объекта MutableCallSite.
Взаимодействие getTarget с памятью идентично чтению из обычной переменной, такой как элемент массива или не-volatile, не-final поле.
В частности, текущий поток может выбрать повторное использование результата предыдущего чтения целевого метода из памяти и может не заметить недавнее обновление целевого метода другим потоком.
- Определено в:
-
getTargetв классеCallSite - Возвращает:
- состояние связи этого объекта вызова — метод-обработчик, который может изменяться со временем
- См. также:
setTarget(java.lang.invoke.MethodHandle)
setTarget
public void setTarget(MethodHandle newTarget)
Обновляет целевой метод этого объекта вызова, как обычную переменную. Тип нового целевого метода должен совпадать с типом старого целевого метода.
Взаимодействие с памятью такое же, как при записи в обычную переменную, например, в элемент массива или в не-volatile, не-final поле.
В частности, независимые потоки могут не заметить обновлённый целевой метод, пока не выполнят чтение из памяти. Более сильные гарантии могут быть созданы путём добавления соответствующих операций в метод инициализации и/или целевые методы, используемые в данном объекте вызова.
- Определено в:
-
setTargetв классеCallSite - Параметры:
-
newTarget— новый целевой метод - Исключения:
-
NullPointerException— если предложенный новый целевой метод равен null -
WrongMethodTypeException— если у предложенного нового целевого метода тип метода отличается от предыдущего целевого метода - См. также:
getTarget()
dynamicInvoker
public final MethodHandle dynamicInvoker()
Создаёт метод-обработчик, эквивалентный инструкции invokedynamic, которая была связана с этим объектом вызова.
Этот метод эквивалентен следующему коду:
MethodHandle getTarget, invoker, result; getTarget = MethodHandles.publicLookup().bind(this, "getTarget", MethodType.methodType(MethodHandle.class)); invoker = MethodHandles.exactInvoker(this.type()); result = MethodHandles.foldArguments(invoker, getTarget)
- Определено в:
-
dynamicInvokerв классеCallSite - Возвращает:
- метод-обработчик, который всегда вызывает текущий целевой метод этого объекта вызова
syncAll
public static void syncAll(MutableCallSite[] sites)
Выполняет операцию синхронизации для каждого объекта вызова в заданном массиве, заставляя все другие потоки отбрасывать любые кэшированные значения, ранее загруженные из целевого метода любого из объектов вызова.
Эта операция не отменяет вызовы, которые уже начались со старым значением целевого метода. (Java поддерживает движение вперёд во времени только.)
Общий эффект заключается в том, что все будущие читатели целевого метода каждого объекта вызова будут принимать последнее сохранённое значение. («Последнее» определяется относительно самого syncAll.) И наоборот, вызов syncAll может блокироваться, пока все читатели не (как-то) удалят все предыдущие версии целевого метода каждого объекта вызова.
Чтобы избежать гонок, вызовы setTarget и syncAll обычно должны выполняться под какой-либо формой взаимной эксклюзивности. Обратите внимание, что потоки-читатели могут наблюдать обновлённый целевой метод уже в момент вызова setTarget, который устанавливает значение (и до вызова syncAll, который подтверждает значение). С другой стороны, потоки-читатели могут наблюдать предыдущие версии целевого метода до возвращения вызова syncAll (и после вызова setTarget, который пытается передать обновлённую версию).
Эта операция, вероятно, будет дорогостоящей и должна использоваться экономно. Если возможно, она должна быть буферизована для обработки набора объектов вызова.
Если sites содержит null-элемент, будет вызвано исключение NullPointerException. В этом случае некоторые не-null элементы в массиве могут быть обработаны до аномального завершения метода. Какие это элементы (если есть) зависит от реализации.
Подробности модели памяти Java
С точки зрения модели памяти Java, эта операция выполняет действие синхронизации, по своему эффекту сравнимое с записью volatile переменной текущим потоком и последующим чтением этой переменной всеми другими потоками, которые могут обратиться к одному из затронутых объектов вызова.Следующие эффекты наблюдаются для каждого отдельного объекта вызова S:
- Создаётся новая volatile переменная
V, и записывается текущим потоком. Как определено в JMM, эта запись является глобальным событием синхронизации. - Как обычно с локальным упорядочением событий записи, каждое действие, уже выполненное текущим потоком, считается происходящим до volatile записи в
V. (В некоторых реализациях это означает, что текущий поток выполняет глобальную операцию release.) - В частности, запись в текущий целевой метод объекта
Sсчитается происходящей до volatile записи вV. - Volatile запись в
Vпомещается (по способу, зависящему от реализации) в глобальный порядок синхронизации. - Рассмотрим произвольный поток
T(отличный от текущего потока). ЕслиTвыполняет действие синхронизацииAпосле volatile записи вV(в глобальном порядке синхронизации), то он, следовательно, должен видеть либо текущий целевой метод объектаS, либо более позднюю запись в этот целевой метод, если он выполняет чтение целевого метода объектаS. (Это ограничение называется "согласованностью порядка синхронизации"). - JMM специально разрешает оптимизирующим компиляторам исключать чтение или запись переменных, которые известны как бесполезные. Такие исключённые чтения и записи не влияют на отношение happens-before. Несмотря на этот факт, volatile переменная
Vне будет исключена, даже если её записанное значение неопределено, а её считанное значение не используется.
T выполняет volatile чтение переменной V сразу после своего действия A. В локальном порядке действий в T это чтение происходит до любого последующего чтения целевого метода объекта S. Будто реализация произвольно выбирает чтение целевого метода объекта S потоком T и принуждает чтение переменной V произойти раньше, тем самым гарантируя передачу нового значения целевого метода. До тех пор, пока соблюдаются ограничения модели памяти Java, реализации могут откладывать завершение операции syncAll, в то время как другие потоки (T выше) продолжают использовать предыдущие значения целевого метода объекта S. Однако реализации по-прежнему рекомендуются избегать тупиков и в конечном итоге требовать от всех потоков учитывать обновлённый целевой метод.
Обсуждение: По соображениям производительности, syncAll не является виртуальным методом для одного объекта вызова, а скорее относится к набору объектов вызова. Некоторые реализации могут понести большие фиксированные накладные расходы для обработки одной или нескольких операций синхронизации, но небольшие дополнительные затраты на каждый дополнительный объект вызова. В любом случае, эта операция, вероятно, будет дорогостоящей, поскольку другим потокам может потребоваться как-то прервать их, чтобы заставить их заметить обновлённое значение целевого метода. Однако можно наблюдать, что единственный вызов синхронизации нескольких объектов имеет тот же формальный эффект, что и множество вызовов, каждый из которых только для одного объекта.
Примечание по реализации: Простые реализации MutableCallSite могут использовать переменную volatile для целевого метода объекта mutable call site. В такой реализации метод syncAll может быть пустым, и тем не менее он будет соответствовать описанному выше поведению JMM.
- Параметры:
-
sites— массив объектов вызова, которые необходимо синхронизировать - Исключения:
-
NullPointerException— если ссылка на массивsitesравна null или массив содержит null
© 1993, 2020, Oracle and/or its affiliates. All rights reserved.
Documentation extracted from Debian's OpenJDK Development Kit package.
Licensed under the GNU General Public License, version 2, with the Classpath Exception.
Various third party code in OpenJDK is licensed under different licenses (see Debian package).
Java and OpenJDK are trademarks or registered trademarks of Oracle and/or its affiliates.