Question

Est-il possible de mettre en œuvre un type de référence dont la valeur peut être échangé avec un autre atomiquement?


En Java, nous avons AtomicReference qui peut être échangé avec une variable locale, mais pas avec un autre AtomicReference.

Vous pouvez faire:

AtomicReference r1 = new AtomicReference("hello");
AtomicReference r2 = new AtomicReference("world");

et d'échanger entre eux avec une combinaison de deux opérations:

r1.set(r2.getAndSet(r1.get()));

Mais cela ne leur laisse dans un état incohérent entre les deux, où les deux contiennent "hello". De plus, même si vous pouvez les échanger atomiquement, vous pouvez toujours pas les lire (comme une paire) atomiquement.


Ce que je voudrais être en mesure de faire est:

PairableAtomicReference r1 = new PairableAtomicReference("hello");
PairableAtomicReference r2 = new PairableAtomicReference("world");
AtomicRefPair rp = new AtomicRefPair(r1, r2);

puis

Object[] oldVal, newVal;
do {
    oldVal = rp.get();
    newVal = new Object[] {oldVal[1], oldVal[0]};
} while (! rp.compareAndSet(oldVal, newVal));

pour échanger les valeurs, et dans un autre thread:

AtomicRefPair otherRP = new AtomicRefPair(r1, r2);
System.out.println(Arrays.toString(otherRP.get()));

et être certain que la sortie sera soit [hello, world] ou [world, hello].

Notes:

  • r1 et r2 sont appairés pour cette opération, mais il est possible qu'un autre thread paire indépendamment, par exemple r1 et un autre r3 (malheureusement cela signifie que je ne peut pas utiliser cette solution .)
  • Il y aura des centaines de milliers de ces références, donc un ReentrantLock mondial serait un goulot d'étranglement majeur.
  • rp et otherRP ne sont pas nécessairement partagées entre les threads, donc simplement de les enfermer ne fonctionnera pas. Ils pourraient être interné , mais la piscine interne aurait besoin de sa propre synchronisation qui serait un autre goulot d'étranglement.
  • Je n'ai fait 2 groupes de références ici, mais la capacité de groupe 3 ou plus serait un bonus.

Est-il possible de mettre en œuvre une version sans verrou de AtomicRefPair? J'ai le pressentiment que ce n'est pas, mais si pas alors peut-être il y a un endroit de l'article qui explique pourquoi?


Related : Comment puis-je échange atomiquement 2 ints en C #

Était-ce utile?

La solution

Je ne sais pas s'il y a une solution agréable, mais le laid suivant pourrait fonctionner:

public final class MyReference<T> extends ReentrantLock implements Comparable<MyReference<T>> {
    public MyReference() {
        id = counter.incrementAndGet();
    }

    public void swap(MyReference<T> other) {
        if (id < other.id) {
            lock();
            other.lock();
        } else {
            other.lock();
            lock();
        }
        final T tmp = value;
        value = other.value;
        other.value = tmp;
        unlock();
        other.unlock();
    }

    public static <T> List<T> consistentGet(List<MyReference<T>> references) {
        final ArrayList<MyReference<T>> sortedReferences = Lists.newArrayList(references);
        Collections.sort(sortedReferences);
        for (val r : sortedReferences) r.lock();
        final List<T> result = Lists.newArrayListWithExpectedSize(sortedReferences.size());
        for (val r : references) result.add(r.value);
        for (val r : sortedReferences) r.unlock();
        return result;
    }

    @Override
    public int compareTo(MyReference<T> o) {
        return id < o.id ? -1 : id > o.id ? 1 : 0;
    }

    private final static AtomicInteger counter = new AtomicInteger();

    private T value;
    private final int id;
}
  • Utiliser MyReference au lieu de AtomicReference.
  • Il utilise beaucoup de serrures, mais aucun d'entre eux est mondiale.
  • Il acquiert des verrous dans un ordre fixe, il est donc sans blocage.
  • Il compile en utilisant lombok et goyave (prendre comme pseudocode sans eux).

Autres conseils

Avoir une classe immuable tenant la paire. C'est votre atome. Permutant les moyens de paires remplaçant l'atome.

Mise à jour: votre question n'est pas très claire. mais en général, pour un système concurrent composé de plusieurs variables, on peut vouloir

  1. prendre un instantané de l'état du système. l'instantané ne change pas une fois pris.
  2. Mise à jour atomiquement état du système en changeant plusieurs variables à la fois. il peut être nécessaire qu'il n'y a pas d'autre mise à jour entre ma mise à jour d'un un instantané précédent (que mon calcul était basé sur)

vous pouvez modéliser votre système directement dans les instantanés, si elle ne consomme pas trop de ressources.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top