Java 9 Variable Handles entmystifiziert
1. Einführung
Java 9 brachte eine Reihe neuer nützlicher Funktionen für Entwickler.
Eine davon ist diejava.lang.invoke.VarHandle-API, die variable Handles darstellt und die wir in diesem Artikel untersuchen werden.
2. Was sind variable Handles?
Im Allgemeinena variable handle is just a typed reference to a variable. Die Variable kann ein Array-Element, eine Instanz oder ein statisches Feld der Klasse sein.
Die KlasseVarHandlebietet unter bestimmten Bedingungen Schreib- und Lesezugriff auf Variablen.
VarHandles sind unveränderlich und haben keinen sichtbaren Zustand. Darüber hinaus können sie nicht in Unterklassen eingeteilt werden.
JedesVarHandle hat:
-
ein generischer TypT, der der Typ jeder Variablen ist, die durch dieseVarHandle dargestellt wird
-
Eine Liste der KoordinatentypenCT, bei denen es sich um Arten von Koordinatenausdrücken handelt, mit denen Variablen gefunden werden können, auf die dieseVarHandle verweisen
Die Liste der Koordinatentypen ist möglicherweise leer.
Das Ziel vonVarHandle ist es, einen Standard zum Aufrufen vonequivalents ofjava.util.concurrent.atomic- undsun.misc.Unsafe-Operationen für Felder und Array-Elemente zu definieren.
Diese Operationen sind mehrheitlich atomare oder geordnete Operationen. Zum Beispiel Atomfeldinkrementierung.
3. Variable Handles erstellen
UmVarHandle zu verwenden, müssen zuerst Variablen vorhanden sein.
Deklarieren wir eine einfache Klasse mit verschiedenen Variablen vom Typint, die wir in unseren Beispielen verwenden werden:
public class VariableHandlesTest {
public int publicTestVariable = 1;
private int privateTestVariable = 1;
public int variableToSet = 1;
public int variableToCompareAndSet = 1;
public int variableToGetAndAdd = 0;
public byte variableToBitwiseOr = 0;
}
3.1. Variablenhandles für öffentliche Variablen
Now we can get a VarHandle for our publicTestVariable using the findVarHandle() method:
VarHandle publicIntHandle = MethodHandles.lookup()
.in(VariableHandlesTest.class)
.findVarHandle(VariableHandlesTest.class, "publicTestVariable", int.class);
assertThat(
publicIntHandle.coordinateTypes().size() == 1);
assertThat(
publicIntHandle.coordinateTypes().get(0) == VariableHandles.class);
Wir können sehen, dasscoordinateTypes diesesVarHandle nicht leer ist und ein Element hat, nämlich unsereVariableHandlesTest-Klasse.
3.2. Variablenhandles für private Variablen
Wenn wir ein privates Mitglied haben und ein variables Handle für eine solche Variablewe can obtain this using the privateLookupIn() methodbenötigen: s:
VarHandle privateIntHandle = MethodHandles
.privateLookupIn(VariableHandlesTest.class, MethodHandles.lookup())
.findVarHandle(VariableHandlesTest.class, "privateTestVariable", int.class);
assertThat(privateIntHandle.coordinateTypes().size() == 1);
assertThat(privateIntHandle.coordinateTypes().get(0) == VariableHandlesTest.class);
Hier haben wir dieprivateLookupIn()-Methode gewählt, die einen breiteren Zugriff als die normalenlookup(). hat. Dies ermöglicht uns den Zugriff aufprivate, public- oderprotected-Variablen.
Vor Java 9 war die entsprechende API für diese Operation die MethodeUnsafeder Klasse_ and the _setAccessible()der MethodeReflection.
Dieser Ansatz hat jedoch seine Nachteile. Beispielsweise funktioniert es nur für die bestimmte Instanz der Variablen.
VarHandle ist in solchen Fällen eine bessere und schnellere Lösung.
3.3. Variable Handles für Arrays
Wir könnten die vorherige Syntax verwenden, um Array-Felder zu erhalten.
Wir können jedoch auchVarHandle für ein Array eines bestimmten Typs abrufen:
VarHandle arrayVarHandle = MethodHandles.arrayElementVarHandle(int[].class);
assertThat(arrayVarHandle.coordinateTypes().size() == 2);
assertThat(arrayVarHandle.coordinateTypes().get(0) == int[].class);
Wir können nun sehen, dass solcheVarHandle zwei Koordinatentypenint und[] haben, die ein Array vonint Primitiven darstellen.
4. Aufrufen vonVarHandle Methoden
Most of the VarHandle methods expect a variable number of arguments of typeObject. Die Verwendung vonObject… als Argument deaktiviert die statische Argumentprüfung.
Alle Argumente werden zur Laufzeit überprüft. Außerdem erwarten verschiedene Methoden eine unterschiedliche Anzahl von Argumenten unterschiedlicher Typen.
Wenn wir nicht die richtige Anzahl von Argumenten mit den richtigen Typen angeben, gibt der MethodenaufrufWrongMethodTypeException. aus
Zum Beispiel erwartetget() mindestens ein Argument, das beim Auffinden der Variablen hilft, aberset() erwartet ein weiteres Argument, das der Variable zugewiesen wird.
5. Zugriffsmodi mit variablen Handles
Im Allgemeinen fallen alle Methoden der Klasse __ vonVarHandleauf fünf verschiedene Zugriffsmodi.
Lassen Sie uns jeden einzelnen in den nächsten Unterabschnitten durchgehen.
5.1. Lesezugriff
Methoden mit Lesezugriffsebene ermöglichen es, den Wert der Variablen unter angegebenen Auswirkungen auf die Speicherreihenfolge abzurufen. Es gibt verschiedene Methoden für diesen Zugriffsmodus:get(), getAcquire(), getVolatile() undgetOpaque().
Wir können leicht dieget()-Methode für unsereVarHandle verwenden:
assertThat((int) publicIntHandle.get(this) == 1);
Dieget()-Methode verwendet nurCoordinateTypes als Parameter, daher können wir in unserem Fall einfachthis verwenden.
5.2. Schreibzugriff
Mit Methoden mit Schreibzugriffsebene können wir den Wert der Variablen unter bestimmten Auswirkungen auf die Speicherreihenfolge festlegen.
Ähnlich wie bei Methoden mit Lesezugriff gibt es verschiedene Methoden mit Schreibzugriff:set(), setOpaque(), setVolatile() undsetRelease().
Wir können dieset()-Methode für unsereVarHandle verwenden:
publicIntHandle.set(this, 15);
assertThat((int) publicIntHandle.get(this) == 15);
Die Methodeset()erwartet mindestens zwei Argumente. Der erste hilft beim Auffinden der Variablen, während der zweite der Wert ist, der für die Variable festgelegt werden soll.
5.3. Atomic Update Access
Methoden mit dieser Zugriffsebene können verwendet werden, um den Wert der Variablen atomar zu aktualisieren.
Verwenden wir diecompareAndSet()-Methode, um die Auswirkungen zu sehen:
publicIntHandle.compareAndSet(this, 1, 100);
assertThat((int) publicIntHandle.get(this) == 100);
Abgesehen vonCoordinateTypes, tnimmt die MethodecompareAndSet()zwei zusätzliche Werte an:oldValue undnewValue. Die Methode legt den Wert der Variablen fest, wenn er gleicholdVariable oder Blättern war es blieb sonst unverändert.
5.4. Numerischer Atomic Update-Zugriff
Diese Methoden ermöglichen das Ausführen numerischer Operationen wiegetAndAdd () unter bestimmten Speicherreihenfolgeeffekten.
Mal sehen, wie wir atomare Operationen mitVarHandle ausführen können:
int before = (int) publicIntHandle.getAndAdd(this, 200);
assertThat(before == 0);
assertThat((int) publicIntHandle.get(this) == 200);
Hier gibt die MethodegetAndAdd()zuerst den Wert der Variablen zurück und addiert dann den angegebenen Wert.
5.5. Bitweiser Atomic Update-Zugriff
Methoden mit diesem Zugriff ermöglichen es, bitweise Operationen unter bestimmten Speicherordnungseffekten atomar auszuführen.
Sehen wir uns ein Beispiel für die Verwendung der MethodegetAndBitwiseOr()an:
byte before = (byte) publicIntHandle.getAndBitwiseOr(this, (byte) 127);
assertThat(before == 0);
assertThat(variableToBitwiseOr == 127);
Diese Methode ermittelt den Wert unserer Variablen und führt eine bitweise ODER-Verknüpfung durch.
Der Methodenaufruf löst einIllegalAccessException aus, wenn der für die Methode erforderliche Zugriffsmodus nicht mit dem von der Variablen zugelassenen übereinstimmt.
Dies geschieht beispielsweise, wenn wir versuchen, eineset()-Methode für einefinal-Variable zu verwenden.
6. Speicherreihenfolgeeffekte
Wir haben zuvor erwähnt, dassVarHandle methods allow access to variables under specific memory ordering effects.
Für die meisten Methoden gibt es 4 Speicherordnungseffekte:
-
Plain Lese- und Schreibvorgänge garantieren eine bitweise Atomizität für Referenzen und Grundelemente unter 32 Bit. Sie unterwerfen auch keine Ordnungsbeschränkungen in Bezug auf die anderen Merkmale.
-
Opaque Operationen sind bitweise atomar und in Bezug auf den Zugriff auf dieselbe Variable kohärent geordnet.
-
Die Operationen vonAcquire undReleaseentsprechen den Eigenschaften vonOpaque. Außerdem werdenAcquire Lesevorgänge erst sortiert, nachdem die Schreibvorgänge imRelease-Modus übereinstimmen.
-
Volatile Operationen sind vollständig in Bezug zueinander geordnet.
Es ist sehr wichtig, sich daran zu erinnern, dassaccess modes will override previous memory ordering effects. Dies bedeutet, dass wenn wir beispielsweiseget() verwenden, dies eine einfache Leseoperation ist, selbst wenn wir unsere Variable alsvolatile. deklariert haben
Aus diesem Grund müssen Entwickler äußerst vorsichtig sein, wenn sieVarHandle-Operationen verwenden.
7. Fazit
In diesem Tutorial stellten wir variable Handles und deren Verwendung vor.
Dieses Thema ist recht kompliziert, da variable Handles eine Manipulation auf niedriger Ebene ermöglichen sollen und nicht verwendet werden sollten, es sei denn, dies ist erforderlich.
Wie immer sind die Codebeispieleover on GitHub verfügbar.