Tham chiếu dễ bay hơi của Java so với AtomicReference


135

Có sự khác biệt nào giữa một volatiletham chiếu Object và AtomicReferencetrong trường hợp tôi sẽ chỉ sử dụng get()và đối set()xứng từ AtomicReference?

Câu trả lời:


114

Câu trả lời ngắn gọn là: Không.

Từ java.util.concurrent.atomictài liệu gói. Để trích:

Các hiệu ứng bộ nhớ để truy cập và cập nhật các nguyên tử thường tuân theo các quy tắc cho các chất bay hơi:

  • getcó hiệu ứng bộ nhớ của việc đọc một volatilebiến.
  • setcó các hiệu ứng bộ nhớ của việc viết (gán) một volatilebiến.

Nhân tiện, tài liệu đó là rất tốt và mọi thứ được giải thích.


AtomicReference::lazySetlà một hoạt động mới hơn (Java 6+) được giới thiệu có ngữ nghĩa không thể thực hiện được thông qua volatilecác biến. Xem bài đăng này để biết thêm thông tin.


11
Và câu trả lời dài hơn sẽ là gì?
Julien Grenier

Đã đồng ý. Chúng tôi ít nhất cần một liên kết.
Julien Chastang


42

Không có.

Sức mạnh bổ sung được cung cấp bởi AtomicReference là phương thức so sánhAndset () và bạn bè. Nếu bạn không cần các phương thức đó, một tham chiếu dễ bay hơi cung cấp cùng một ngữ nghĩa như AtomicReference.set () và .get ().


14

Có một số khác biệt và sự đánh đổi:

  1. Sử dụng một AtomicReferenceget / set có cùng ngữ nghĩa JMM như một trường biến động (như các trạng thái javadoc), nhưng AtomicReferencelà một trình bao bọc xung quanh một tham chiếu, vì vậy mọi truy cập vào trường đều liên quan đến việc đuổi theo con trỏ .

  2. Các bộ nhớ được nhân (giả sử một môi trường oops nén, đó là đúng đối với hầu hết các máy ảo):

    • ref dễ bay hơi = 4b
    • AtomicReference = 4b + 16b (tiêu đề đối tượng 12b + trường ref 4b)
  3. AtomicReferencecung cấp một API phong phú hơn một tài liệu tham khảo dễ bay hơi. Bạn có thể lấy lại API cho tham chiếu dễ bay hơi bằng cách sử dụng AtomicFieldUpdaterhoặc với Java 9 a VarHandle. Bạn cũng có thể vươn thẳng sun.misc.Unsafenếu bạn thích chạy bằng kéo. AtomicReferencechính nó được thực hiện bằng cách sử dụng Unsafe.

Vì vậy, khi nào thì tốt để chọn cái này hơn cái kia:

  • Chỉ cần get / set? Gắn bó với một lĩnh vực dễ bay hơi, giải pháp đơn giản nhất và chi phí thấp nhất.
  • Cần các chức năng bổ sung? Nếu đây là phần nhạy cảm về hiệu năng (tốc độ / bộ nhớ) trong mã của bạn, hãy lựa chọn giữa AtomicReference/ AtomicFieldUpdater/ Unsafenơi bạn có xu hướng trả tiền để có thể đọc và rủi ro cho hiệu suất của bạn. Nếu đây không phải là một khu vực nhạy cảm chỉ cần đi cho AtomicReference. Các nhà văn thư viện thường sử dụng kết hợp các phương thức này tùy thuộc vào các JDK được nhắm mục tiêu, các hạn chế API dự kiến, các ràng buộc về bộ nhớ, v.v.

7

Mã nguồn JDK là một trong những cách tốt nhất để trả lời những nhầm lẫn như thế này. Nếu bạn nhìn vào mã trong AtomicReference, nó sử dụng biến volatie để lưu trữ đối tượng.

private volatile V value;

Vì vậy, rõ ràng nếu bạn sẽ chỉ sử dụng get () và set () trên AtomicReference thì nó giống như sử dụng một biến dễ bay hơi. Nhưng như các độc giả khác nhận xét, AtomicReference cung cấp thêm ngữ nghĩa CAS. Vì vậy, trước tiên hãy quyết định xem bạn có muốn ngữ nghĩa CAS hay không và nếu bạn chỉ sử dụng thì hãy sử dụng AtomicReference.


13
"Mã nguồn JDK là một trong những cách tốt nhất để trả lời những nhầm lẫn như thế này" => Tôi không nhất thiết phải đồng ý - javadoc (là hợp đồng của lớp) là cách tốt nhất. Những gì bạn tìm thấy trong mã trả lời câu hỏi cho một triển khai cụ thể nhưng mã có thể thay đổi.
assylias

4
Ví dụ , biến này trong hashmap không ổn định trong JDK 6 nhưng không còn biến động nữa trong Java 7. Bạn đã dựa vào mã của mình trên thực tế là biến đó có biến động hay không, nó sẽ bị hỏng khi làm xấu JDK của bạn ... khác nhau nhưng bạn có được điểm.
assylias

Là CAS một số viết tắt tiêu chuẩn?
abbas

1
So sánh và hoán đổi =)
vô tận

4

AtomicReferencecung cấp chức năng bổ sung mà một biến dễ bay hơi không cung cấp. Khi bạn đã đọc API Javadoc, bạn sẽ biết điều này, nhưng nó cũng cung cấp một khóa có thể hữu ích cho một số hoạt động.

Tuy nhiên, trừ khi bạn cần chức năng bổ sung này, tôi khuyên bạn nên sử dụng một volatiletrường đơn giản .


Vì vậy, sự khác biệt, sau đó, là trong hiệu suất của họ. Nếu không có sự khác biệt, bạn sẽ không bao giờ đề nghị sử dụng cái này hơn cái kia.
BT

Hiệu suất là nhiều như nhau. Một AtomicRefrence thêm độ phức tạp và sử dụng bộ nhớ.
Peter Lawrey

@BT Một volatiletrường có thể được sử dụng như bất kỳ trường thông thường nào trong khi truy cập giá trị trong một AtomicReferenceyêu cầu phải trải qua getsetphương thức.
David Harkness

0

Đôi khi ngay cả khi bạn chỉ sử dụng get và sets, AtomicReference có thể là một lựa chọn tốt:

Ví dụ với biến động:

private volatile Status status;
...
public setNewStatus(Status newStatus){
  status = newStatus;
}

public void doSomethingConditionally() {
  if(status.isOk()){
    System.out.println("Status is ok: " + status); // here status might not be OK anymore because in the meantime some called setNewStatus(). setNewStatus should be synchronized
  }
}

Việc triển khai với AtomicReference sẽ cung cấp cho bạn đồng bộ hóa bản sao khi ghi.

private AtomicReference<Status> statusWrapper;
...

public void doSomethingConditionally() {
  Status status = statusWrapper.get();
  if(status.isOk()){
    System.out.println("Status is ok: " + status); // here even if in the meantime some called setNewStatus() we're still referring to the old one
  }
}

Người ta có thể nói rằng bạn vẫn có thể có một bản sao phù hợp nếu bạn thay thế:

Status status = statusWrapper.get();

với:

Status statusCopy = status;

Tuy nhiên, cái thứ hai có khả năng bị xóa bởi một người vô tình trong tương lai trong quá trình "làm sạch mã".

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.