Класс MutableCallSite
- Прямые известные подклассы:
AbstractRelinkableCallSite
public non-sealed class MutableCallSite extends CallSite
CallSite, целевая переменная которого ведет себя как обычное поле. Инструкция MutableCallSite, связанная с 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() |
Возвращает целевой метод вызова, который ведет себя как обычное поле изменяемого вызова. |
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 поля.
В частности, текущая нить может выбрать повторное использование результата предыдущего чтения целевого значения из памяти и может не увидеть недавнее обновление целевого значения другой нитью.
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 есть элемент null, будет выброшено исключение NullPointerException. В этом случае некоторые не-null элементы массива могут быть обработаны до того, как метод вернёт исключение. Какие именно элементы это (если таковые имеются) зависит от реализации.
Подробности модели памяти 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 для целевого значения места вызова. В такой реализации метод syncAll может быть пустым, и тем не менее он будет соответствовать поведению JMM, описанному выше.
- Параметры:
-
sites- массив мест вызова, которые должны быть синхронизированы - Исключения:
-
NullPointerException- если ссылка на массивsitesравна null или массив содержит null
© 1993, 2025, 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://download.java.net/java/early_access/jdk24/docs/api/java.base/java/lang/invoke/MutableCallSite.html