Класс MutableCallSite
- Прямые известные подклассы:
AbstractRelinkableCallSite
public non-sealed class MutableCallSite extends CallSite
MutableCallSite — это CallSite, переменная цели которой ведет себя как обычное поле. Инструкция invokedynamic , связанная с MutableCallSite , делегирует все вызовы текущей цели сайта. Динамический вызывающий метод изменяемого вызова сайта также делегирует каждый вызов текущей цели сайта. Вот пример изменяемого вызова сайта, который вводит переменную состояния в цепочку обращений к методу.
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 вместо этого.
- С момента:
- 1.7
Краткое описание конструкторов
| Конструктор | Описание |
|---|---|
MutableCallSite |
Создаёт объект вызова сайта с начальным целевым методом. |
MutableCallSite |
Создаёт пустой объект вызова сайта с заданным типом метода. |
Краткое описание методов
| Модификатор и тип | Метод | Описание |
|---|---|---|
final MethodHandle |
dynamicInvoker() |
Создаёт обработчик метода, эквивалентный инструкции invokedynamic, которая была связана с этим вызовом сайта. |
final MethodHandle |
getTarget() |
Возвращает целевой метод вызова сайта, который ведёт себя как обычное поле MutableCallSite. |
void |
setTarget |
Обновляет целевой метод этого вызова сайта, как обычную переменную. |
static void |
syncAll |
Выполняет операцию синхронизации для каждого вызова сайта в данном массиве, заставляя все другие потоки отбросить любые кэшированные значения, ранее загруженные из целевого объекта любого из вызовов сайта. |
Подробное описание конструкторов
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
public void setTarget(MethodHandle newTarget)
Взаимодействия с памятью аналогичны записи в обычную переменную, такую как элемент массива или не-volatile, не-final поле.
В частности, несвязанные потоки могут не видеть обновлённого целевого значения до тех пор, пока они не выполнят чтение из памяти. Более сильные гарантии можно создать, поместив соответствующие операции в метод инициализации и/или целевые методы, используемые в данном вызове.
- Определено в:
-
setTargetв классеCallSite - Параметры:
-
newTarget- новое целевое значение - Исключения:
-
NullPointerException- если предложенное новое целевое значение равно null -
WrongMethodTypeException- если предложенный новый целевой метод имеет тип метода, отличный от предыдущего - См. также:
dynamicInvoker
public final MethodHandle dynamicInvoker()
Этот метод эквивалентен следующему коду:
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 содержит нулевой элемент, будет выброшено исключение NullPointerException. В этом случае некоторые ненулевые элементы массива могут быть обработаны до аномального завершения метода. Какие именно элементы это будут (если таковые имеются) — зависит от реализации.
Подробности модели памяти Java
С точки зрения модели памяти Java, эта операция выполняет действие синхронизации, которое по своему эффекту сопоставимо с записью переменной volatile текущим потоком и последующим чтением этой переменной volatile всеми другими потоками, которые могут получить доступ к одному из затронутых вызовов. Видимые эффекты для каждого отдельного вызова S:
- Создаётся новая переменная volatile
V, и записывается текущим потоком. Как определено в JMM, эта запись является глобальным событием синхронизации. - Как обычно для локального упорядочивания событий записи, каждое действие, уже выполненное текущим потоком, считается предшествующим записи volatile в
V. (В некоторых реализациях это означает, что текущий поток выполняет глобальную операцию освобождения.) - В частности, запись в текущее значение цели вызова
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, 2023, 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.
https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/lang/invoke/MutableCallSite.html