有没有办法实现一种类型的引用,其值可以与另一个原子交换?
在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
(不幸的是,这意味着我无法使用this solution。)ReentrantLock
将成为主要瓶颈。rp
和otherRP
不一定在线程之间共享,因此只需锁定它们就行不通。它们可能是interned,但实习池需要自己的同步,这将是另一个瓶颈。是否可以实现AtomicRefPair
的无锁版本?我有预感它不是,但如果没有,那么可能有一篇文章可以解释为什么?
答案 0 :(得分:4)
拥有一对不可变的类。那是你的原子。交换对意味着更换原子。
更新:你的问题不是很清楚。但一般来说,对于由多个变量组成的并发系统,可能需要
如果不消耗太多资源,您可以直接在快照中为系统建模。
答案 1 :(得分:3)
我不知道是否有一个很好的解决方案,但以下丑陋的解决方案可以起作用:
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;
}