Câu trả lời:
Câu trả lời ngắn gọn là: Không.
Từ java.util.concurrent.atomic
tà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:
get
có hiệu ứng bộ nhớ của việc đọc mộtvolatile
biến.set
có các hiệu ứng bộ nhớ của việc viết (gán) mộtvolatile
biến.
Nhân tiện, tài liệu đó là rất tốt và mọi thứ được giải thích.
AtomicReference::lazySet
là 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 volatile
các biến. Xem bài đăng này để biết thêm thông tin.
Có một số khác biệt và sự đánh đổi:
Sử dụng một AtomicReference
get / 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 AtomicReference
là 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ỏ .
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):
AtomicReference
= 4b + 16b (tiêu đề đối tượng 12b + trường ref 4b)AtomicReference
cung 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 AtomicFieldUpdater
hoặc với Java 9 a VarHandle
. Bạn cũng có thể vươn thẳng sun.misc.Unsafe
nếu bạn thích chạy bằng kéo. AtomicReference
chí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:
AtomicReference
/ AtomicFieldUpdater
/ Unsafe
nơ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.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.
AtomicReference
cung 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 volatile
trường đơn giản .
volatile
trườ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 AtomicReference
yêu cầu phải trải qua get
và set
phương thức.
Đô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ã".