Возможно создать AtomicReference, который может быть обменен атомарно?
Есть ли способ реализовать тип ссылки, значение которой может быть обменено на другой атомно?
В Java у нас есть AtomicReference
который может быть заменен локальной переменной, но не другой AtomicReference
,
Ты можешь сделать:
AtomicReference r1 = new AtomicReference("hello");
AtomicReference r2 = new AtomicReference("world");
и поменять их комбинацией двух операций:
r1.set(r2.getAndSet(r1.get()));
Но это оставляет их в противоречивом состоянии между ними, где оба содержат "hello"
, Кроме того, даже если вы можете поменять их атомарно, вы все равно не сможете прочитать их (как пару) атомарно.
То, что я хотел бы сделать, это:
PairableAtomicReference r1 = new PairableAtomicReference("hello");
PairableAtomicReference r2 = new PairableAtomicReference("world");
AtomicRefPair rp = new AtomicRefPair(r1, r2);
затем
Object[] oldVal, newVal;
do {
oldVal = rp.get();
newVal = new Object[] {oldVal[1], oldVal[0]};
} while (! rp.compareAndSet(oldVal, newVal));
поменять местами значения и в другом потоке:
AtomicRefPair otherRP = new AtomicRefPair(r1, r2);
System.out.println(Arrays.toString(otherRP.get()));
и быть уверенным, что выход будет либо [hello, world]
или же [world, hello]
,
Заметки:
r1
а такжеr2
в паре для этой операции, но возможно, что другой поток будет независимо, скажем,r1
и другойr3
(к сожалению, это означает, что я не могу использовать это решение.)- Там будут сотни тысяч этих ссылок, поэтому глобальный
ReentrantLock
будет основным узким местом. rp
а такжеotherRP
не обязательно разделяются между потоками, поэтому простое их блокирование не будет работать. Они могут быть интернированы, но для внутреннего пула потребуется собственная синхронизация, что станет еще одним узким местом.- Я только сделал группы из 2 ссылок здесь, но возможность группировать 3 или более будет бонусом.
Можно ли реализовать версию без блокировки AtomicRefPair
? У меня есть догадка, что это не так, но если нет, то, возможно, где-то есть статья, которая объясняет, почему?
Связанный: Как я могу атомарно поменять 2 ints в C#?
2 ответа
Я не знаю, есть ли хорошее решение, но могло бы работать следующее безобразное:
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;
}
- Используйте MyReference вместо AtomicReference.
- Он использует много замков, но ни один из них не является глобальным.
- Он получает блокировки в фиксированном порядке, поэтому он не блокируется.
- Он компилируется с использованием lombok и guava (воспринимайте его как псевдокод без них).
Имейте неизменный класс, держащий пару. Это твой атом. Обмен пары означает замену атома.
обновление: ваш вопрос не очень понятен. но в целом для параллельной системы, состоящей из нескольких переменных, можно
- сделать снимок состояния системы. Снимок не меняется после съемки.
- атомное обновление состояния системы путем одновременного изменения нескольких переменных. может потребоваться, чтобы между моим обновлением и предыдущим снимком не было другого обновления (на котором основывались мои вычисления)
Вы можете смоделировать свою систему непосредственно в моментальных снимках, если она не потребляет слишком много ресурсов.