Java 9 Variable behandelt entmystifiziert

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.