2011-01-25 29 views
6

Có cách nào để thực hiện một loại tham chiếu có giá trị có thể được trao đổi với một nguyên tử khác không?Có thể tạo AtomicReference có thể hoán đổi nguyên tử không?


Trong Java chúng tôi có AtomicReference có thể được hoán đổi với một biến địa phương nhưng không phải với AtomicReference khác.

Bạn có thể làm:

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

và trao đổi chúng với một sự kết hợp của hai hoạt động:

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

Nhưng điều này làm cho họ trong tình trạng mâu thuẫn ở giữa, nơi cả hai chứa "hello". Thậm chí nếu bạn có thể hoán đổi chúng một cách nguyên tử, bạn vẫn không thể đọc chúng (như một cặp) một cách nguyên tử.


Những gì tôi muốn để có thể làm là:

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

sau đó

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

để trao đổi các giá trị, và trong chủ đề khác:

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

và chắc chắn rằng đầu ra sẽ là [hello, world] hoặc [world, hello].

Ghi chú:

  • r1r2 được ghép nối cho hoạt động này, nhưng nó có thể là một sợi độc lập sẽ ghép, nói r1 và một r3
  • Có (may mà có nghĩa là tôi không thể sử dụng this solution.) sẽ là hàng trăm nghìn tài liệu tham khảo này, vì vậy, ReentrantLock toàn cầu sẽ là một nút cổ chai lớn.
  • rpotherRP không nhất thiết phải được chia sẻ giữa các chuỗi, vì vậy việc chỉ cần khóa chúng sẽ không hoạt động. Chúng có thể là interned, nhưng hồ bơi thực tập sẽ cần sự đồng bộ hóa riêng của nó và đây sẽ là một nút cổ chai khác.
  • Tôi chỉ tạo nhóm 2 tham chiếu ở đây, nhưng khả năng nhóm 3 trở lên sẽ là tiền thưởng.

Có thể triển khai phiên bản không có khóa AtomicRefPair không? Tôi có linh cảm rằng nó không phải là, nhưng nếu không thì có lẽ có một bài báo ở đâu đó giải thích tại sao?


liên quan: How do I atomically swap 2 ints in C#?

+1

Có một Thực tập sinh ở ổi, sử dụng ConcurrentHashMap, do đó, tranh chấp có thể nhỏ tùy ý ở mức trung bình. – maaartinus

Trả lời

3

Tôi không biết nếu có một giải pháp tốt đẹp, nhưng một trong những xấu xí sau đây có thể làm việ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; 
} 
  • Sử dụng MyReference thay vì AtomicReference.
  • Nó sử dụng nhiều khóa, nhưng không có khóa nào là toàn cầu.
  • Nó mua lại khóa theo thứ tự cố định, vì vậy nó không có khóa.
  • Nó biên dịch bằng cách sử dụng lombok và ổi (coi nó là mã giả nếu không có chúng).
+0

+1. Hầu như những gì tôi muốn đề nghị. Tuy nhiên op cần "hàng trăm ngàn tài liệu tham khảo". Có lẽ nó sẽ là tốt hơn để nhóm chúng vào phân vùng và liên kết ổ khóa với các phân vùng này, gần như giống như cách 'ConcurrentHashMap' hoạt động. Nếu số lượng phân vùng là hợp lý lớn, xác suất tranh chấp sẽ thấp. – axtavt

+0

Tôi sẽ không, vì ReentrantLock là cổ tích nhỏ vì nó chỉ chứa một tham chiếu đến một đối tượng trống. Vì vậy, ngay cả một triệu người trong số họ chỉ tốn 24 MB (giả định 8B cho mỗi tham chiếu và 16B cho mỗi đối tượng). – maaartinus

+0

@maaartinus, tham chiếu đến lombok ở đâu? – finnw

4

Có lớp không thay đổi giữ cặp. Đó là nguyên tử của bạn. Trao đổi cặp có nghĩa là thay thế nguyên tử.

cập nhật: câu hỏi của bạn không rõ ràng lắm. nhưng nói chung, đối với một hệ thống đồng thời bao gồm nhiều biến, người ta có thể muốn

  1. chụp nhanh trạng thái hệ thống. ảnh chụp nhanh không thay đổi khi chụp.
  2. cập nhật trạng thái hệ thống bằng cách thay đổi nhiều biến cùng một lúc. có thể yêu cầu không có bản cập nhật nào khác giữa bản cập nhật của tôi một ảnh chụp nhanh trước đó (tính toán của tôi dựa trên)

bạn có thể lập mô hình hệ thống của mình trực tiếp trong ảnh chụp nhanh, nếu nó không tiêu tốn quá nhiều tài nguyên.

+0

Xem dấu đầu dòng trong câu hỏi. – finnw

+0

@finnw - có gì sai với câu trả lời này? đây là một giải pháp hợp lý (và ít phức tạp hơn). – jtahlborn

+0

Tôi đã đề cập đến một thực tế rằng 'r1' và' r2' có thể được ghép nối cho một hoạt động và 'r1' và' r3' có thể được ghép nối với nhau. Điều này có thể làm việc về nguyên tắc nhưng nó sẽ cần một cách để tự động nhóm lại các tài liệu tham khảo. – finnw

Các vấn đề liên quan