web-dev-qa-db-fra.com

Bonne façon d'utiliser VarHandle dans Java 9?

J'ai passé beaucoup de temps à étudier certaines des Java 9 nouvelles fonctionnalités, mais je n'ai trouvé aucun exemple utile et pratique.

Considérez l'extrait de code suivant qui crée un VarHandle:

class Counter {
    int i;
}

class VarHandleInAction {
    static final VarHandle VH_COUNTER_FIELD_I;

    static {
        try {
            VH_COUNTER_FIELD_I = MethodHandles.lookup()
                .in(Counter.class)
                .findVarHandle(Counter.class, "i", int.class);
        } catch (Exception e) {
            // ...
        }
    }
}

Mais quelle est la prochaine étape? Je veux dire, comment utiliser cela poignée variable? Pouvez-vous fournir de vrais exemples?

30
Oleksandr Pyrohov

Il est utilisé par exemple dans AtomicReference, où précédemment dans Java 8, Sun.misc.Unsafe a été utilisé:

public final void lazySet(V newValue) {
    unsafe.putOrderedObject(this, valueOffset, newValue);
}

public final boolean compareAndSet(V expect, V update) {
    return unsafe.compareAndSwapObject(this, valueOffset, expect, update);
}

Ici, le pointeur this est utilisé avec un décalage de champ pour accéder au champ. Mais ce n'est pas sûr, car ce décalage de champ pourrait être n'importe quel long, et vous pourriez en fait accéder à quelque chose de complètement différent. il y a cependant des avantages en termes de performances à le faire de cette façon (il indique au VM d'utiliser des instructions CPU spécialisées par exemple), et à cause de cela, d'autres personnes ont utilisé Sun.misc.Unsafe même s'il s'agit d'une API interne et dangereuse .

VarHandles sert en partie à remplacer les opérations dans Sun.misc.Unsafe avec un équivalent sûr. Ce qui est indiqué dans le JEP :

Définissez un moyen standard d'invoquer les équivalents de diverses opérations Java.util.concurrent.atomic et Sun.misc.Unsafe ...

Buts:

Les objectifs suivants sont obligatoires:

  • Sécurité. Il ne doit pas être possible de placer la machine virtuelle Java dans un état de mémoire corrompu. Par exemple, un champ d'un objet ne peut être mis à jour qu'avec des instances pouvant être castées selon le type de champ, ou un L'élément tableau n'est accessible dans un tableau que si l'index du tableau se trouve dans les limites du tableau.

  • Intégrité. L'accès à un champ d'un objet suit les mêmes règles d'accès qu'avec les codes d'octet getfield et putfield en plus de la contrainte qu'un champ final d'un objet ne peut pas être mis à jour. (Remarque: ces règles de sécurité et d'intégrité s'appliquent également aux MethodHandles donnant un accès en lecture ou en écriture à un champ.)

  • Performance. Les caractéristiques de performance doivent être identiques ou similaires aux opérations Sun.misc.Unsafe équivalentes (en particulier, le code assembleur généré doit être presque identique modulo certains contrôles de sécurité qui ne peuvent pas être repliés).

  • Convivialité. L'API doit être meilleure que l'API Sun.misc.Unsafe.

Donc en Java 9 ces méthodes ressemblent à ceci:

public final void lazySet(V newValue) {
    VALUE.setRelease(this, newValue);
}

public final boolean compareAndSet(V expectedValue, V newValue) {
    return VALUE.compareAndSet(this, expectedValue, newValue);
}

VALUE est un VarHandle définir comme ceci:

private static final VarHandle VALUE;
static {
    try {
        MethodHandles.Lookup l = MethodHandles.lookup();
        VALUE = l.findVarHandle(AtomicReference.class, "value", Object.class);
    } catch (ReflectiveOperationException e) {
        throw new Error(e);
    }
}
31
Jorn Vernee