2011-01-25 45 views
6

有沒有什麼辦法可以實現一種參考類型的值可以與另一個原子交換?可能創建可以原子交換的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]

注:

  • r1r2配對進行此操作,但它可能是另一個線程將獨立配對,說r1和另一r3
  • 有(不幸的是,這意味着我不能使用this solution)。將有成千上萬的這些參考文獻,因此全球性的ReentrantLock將是一個主要的瓶頸。
  • rpotherRP不一定在線程之間共享,所以只需鎖定它們將不起作用。他們可能是interned,但實習生池將需要自己的同步,這將是另一個瓶頸。
  • 我在這裏只做了2個參考組,但能夠組3或更多將是一個獎金。

是否可以實現無鎖版本AtomicRefPair?我有一個預感,它不是,但如果沒有,那麼也許有一篇文章解釋了爲什麼?


相關How do I atomically swap 2 ints in C#?

+1

Guava中有一個Interner,它使用ConcurrentHashMap,所以爭用可以是平均任意小的。 – maaartinus 2011-01-25 22:44:27

回答

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; 
} 
  • 使用MyReference代替的AtomicReference。
  • 它使用了很多鎖,但都不是全局鎖。
  • 它以固定順序獲取鎖,因此它是無死鎖的。
  • 它使用lombok和番石榴編譯(把它當作沒有它們的僞代碼)。
+0

+1。幾乎我想提出的建議。然而,運營商需要「成千上萬的這些參考資料」。也許最好將它們分組到分區中,並將鎖與這些分區關聯起來,這幾乎與`ConcurrentHashMap`的工作方式一樣。如果分區數量相當大,爭用的概率應該很低。 – axtavt 2011-01-25 22:34:22

+0

我不會,因爲ReentrantLock只包含一個對一個空對象的引用,所以它很小。所以即使其中一百萬只需要24 MB(假設每個參考8B和每個對象16B)。 – maaartinus 2011-01-25 22:41:20

4

擁有一個不可變類持有這對。那是你的原子。交換對意味着替換原子。

更新:你的問題不是很清楚。但一般情況下,對於由多個變量組成的併發系統,可能需要

  1. 拍攝系統狀態快照。一旦拍攝,快照不會改變。
  2. 通過一次更改多個變量自動更新系統狀態。可能需要在我的更新和以前的快照之間沒有其他更新(我的計算基於此)

如果不佔用太多資源,則可以在快照中直接建模您的系統。

相關問題