Domanda

C'è un modo per implementare un tipo di riferimento il cui valore può essere scambiato con un altro atomicamente?


In Java abbiamo AtomicReference che può essere scambiato con una variabile locale, ma non con un altro AtomicReference.

Si può fare:

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

e li swap con una combinazione di due operazioni:

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

Ma questo li lascia in uno stato incoerente in mezzo, dove entrambi contengono "hello". Inoltre, anche se si potrebbe scambiarle atomicamente, ancora non poteva leggere loro (come una coppia) atomicamente.


Quello che mi piacerebbe essere in grado di fare è:

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

poi

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

per scambiare i valori, e in un altro thread:

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

ed essere certi che l'uscita sarà [hello, world] o [world, hello].

Note:

  • r1 e r2 sono accoppiati per questa operazione, ma è possibile che un altro thread indipendentemente coppia, dire r1 e un'altra r3 (purtroppo che mezzi non riesco ad usare questa soluzione .)
  • Ci saranno centinaia di migliaia di questi riferimenti, quindi un ReentrantLock globale sarebbe un serio ostacolo.
  • rp e otherRP non sono necessariamente condivise tra thread, così semplicemente bloccando loro non funzionerà. Potrebbero essere internati , ma il pool interno avrebbe bisogno proprio di sincronizzazione che sarebbe un altro collo di bottiglia.
  • ho fatto solo gruppi di 2 referenze qui, ma la capacità di gruppo 3 o più sarebbe un bonus.

E 'possibile implementare una versione senza blocchi di AtomicRefPair? Ho la sensazione che non lo è, ma se non allora forse c'è un qualche articolo che spiega perché?


correlati :? Come faccio atomicamente swap a 2 int in C #

È stato utile?

Soluzione

Non so se c'è una bella soluzione, ma il seguente brutto potrebbe funzionare:

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;
}
  • Usa MyReference invece di AtomicReference.
  • Si utilizza un sacco di serrature, ma nessuno di loro è globale.
  • Si acquisisce i blocchi in un ordine fisso, quindi è priva di stallo.
  • Compila utilizzando Lombok e guava (prendere come pseudocodice senza di loro).

Altri suggerimenti

Avere una classe immutabile, tenendo la coppia. Questo è il vostro atomo. Lo scambio dei mezzi di accoppiamento sostituzione dell'atomo.

Aggiornamento: la tua domanda non è molto chiara. ma in generale, per un sistema concorrente composto da più variabili, uno potrebbe desiderare

  1. scattare un'istantanea dello stato del sistema. l'istantanea non cambia una volta preso.
  2. atomicamente modificare lo stato del sistema modificando variabili multiple contemporaneamente. può essere richiesto che non v'è altro aggiornamento tra il mio aggiornamento di uno snapshot precedente (che il mio calcolo è stato basato su)

è possibile modellare il sistema direttamente in istantanee, se non consuma troppe risorse.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top