Возможно создать атомареференцию, которая может быть атомно обременена?
-
24-10-2019 - |
Вопрос
Есть ли способ реализовать тип ссылки, значение которого можно обменять с другой атомной?
На 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 INT в C#?
Решение
Я не знаю, есть ли хорошее решение, но следующее уродливое можно сработать:
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.
- Он использует много замков, но ни один из них не является глобальным.
- Он приобретает замки в фиксированном порядке, так что он не имеет тупика.
- Он компилирует с использованием Ломбока и гуавы (воспринимайте его как псевдокод без них).
Другие советы
Иметь неизменную класс, держащую пару. Это ваш атом. Обмен парой означает замену атома.
Обновление: ваш вопрос не очень ясен. Но в целом для одновременной системы, состоящей из нескольких переменных, можно хотелось бы хотеть
- Сделайте снимок состояния системы. Снимок не меняется однажды.
- Атомальное обновление системы системы путем изменения нескольких переменных одновременно. Возможно, потребуется, чтобы между моим обновлением не было другого обновления A предыдущего снижения (который был основан на моем расчете)
Вы можете смоделировать свою систему непосредственно в снимках, если она не потребляет слишком много ресурсов.