Pregunta

¿Hay alguna forma de implementar un tipo de referencia cuyo valor se pueda intercambiar con otro atómicamente?


En Java tenemos AtomicReference que se puede cambiar con una variable local pero no con otra AtomicReference.

Tu puedes hacer:

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

y intercambiarlos con una combinación de dos operaciones:

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

Pero esto los deja en un estado inconsistente en el medio, donde ambos contienen "hello". Además, incluso si pudieras intercambiarlos atómicamente, aún no podías leerlos (como un par) atómicamente.


Lo que me gustaría poder hacer es:

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

después

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

Para intercambiar los valores y en otro hilo:

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

y asegúrese de que la salida sea [hello, world] o [world, hello].

Notas:

  • r1 y r2 se emparejan para esta operación, pero es posible que otro hilo se empare de forma independiente, digamos r1 y otro r3 (Desafortunadamente eso significa que no puedo usar esta solución.)
  • Habrá cientos de miles de estas referencias, por lo que un global ReentrantLock Sería un gran cuello de botella.
  • rp y otherRP No se comparten necesariamente entre hilos, por lo que simplemente bloquearlos no funcionará. Ellos pueden ser interno, pero el grupo de pasantes necesitaría su propia sincronización que sería otro cuello de botella.
  • Solo he hecho grupos de 2 referencias aquí, pero la capacidad de Grupo 3 o más sería una ventaja.

¿Es posible implementar una versión sin bloqueo de AtomicRefPair? Tengo un presentimiento de que no lo es, pero si no, ¿tal vez hay un artículo en algún lugar que explique por qué?


Relacionado: ¿Cómo cambio atómicamente 2 ints en C#?

¿Fue útil?

Solución

No sé si hay una buena solución, pero la siguiente fea podría funcionar:

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;
}
  • Use MyReference en lugar de Atomicreference.
  • Utiliza muchas cerraduras, pero ninguno de ellos es global.
  • Adquiere cerraduras en un orden fijo, por lo que no tiene punto muerto.
  • Se compila usando Lombok y Guava (tómalo como pseudocódigo sin ellos).

Otros consejos

Tener una clase inmutable que sostenga el par. Ese es tu átomo. Intercambiar el par significa reemplazar el átomo.

ACTUALIZACIÓN: Su pregunta no es muy clara. Pero en general, para un sistema concurrente que consiste en múltiples variables, uno podría querer

  1. Tome una instantánea del estado del sistema. La instantánea no cambia una vez tomada.
  2. Actualice atómicamente el estado del sistema cambiando múltiples variables a la vez. Es necesario que no haya otra actualización entre mi actualización y una instantánea anterior (en la que mi cálculo se basó)

Puede modelar su sistema directamente en las instantáneas, si no consume demasiados recursos.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top