Điểm quan trọng về volatile
:
- Có thể đồng bộ hóa trong Java bằng cách sử dụng các từ khóa
synchronized
và khóa Java volatile
.
- Trong Java, chúng ta không thể có
synchronized
biến. Sử dụng synchronized
từ khóa với một biến là bất hợp pháp và sẽ dẫn đến lỗi biên dịch. Thay vì sử dụng synchronized
biến trong Java, bạn có thể sử dụng volatile
biến java , sẽ hướng dẫn các luồng JVM đọc giá trị củavolatile
biến từ bộ nhớ chính và không lưu trữ cục bộ vào bộ đệm.
- Nếu một biến không được chia sẻ giữa nhiều luồng thì không cần sử dụng
volatile
từ khóa.
nguồn
Ví dụ sử dụng của volatile
:
public class Singleton {
private static volatile Singleton _instance; // volatile variable
public static Singleton getInstance() {
if (_instance == null) {
synchronized (Singleton.class) {
if (_instance == null)
_instance = new Singleton();
}
}
return _instance;
}
}
Chúng tôi đang tạo ra một cách lười biếng tại thời điểm yêu cầu đầu tiên xuất hiện.
Nếu chúng ta không tạo _instance
biến volatile
thì Thread đang tạo thể hiện của Singleton
không thể giao tiếp với luồng khác. Vì vậy, nếu Thread A đang tạo cá thể Singleton và ngay sau khi tạo, CPU bị hỏng, v.v., tất cả các luồng khác sẽ không thể thấy giá trị của_instance
là không null và họ sẽ tin rằng nó vẫn được gán null.
Lý do tại sao điều này xảy ra? Bởi vì các luồng của trình đọc không thực hiện bất kỳ khóa nào và cho đến khi luồng của trình ghi ra khỏi một khối được đồng bộ hóa, bộ nhớ sẽ không được đồng bộ hóa và giá trị của _instance
sẽ không được cập nhật trong bộ nhớ chính. Với từ khóa Biến động trong Java, điều này được xử lý bởi chính Java và các cập nhật như vậy sẽ được hiển thị bởi tất cả các luồng người đọc.
Kết luận : volatile
từ khóa cũng được sử dụng để truyền đạt nội dung của bộ nhớ giữa các luồng.
Ví dụ sử dụng mà không dễ bay hơi:
public class Singleton{
private static Singleton _instance; //without volatile variable
public static Singleton getInstance(){
if(_instance == null){
synchronized(Singleton.class){
if(_instance == null) _instance = new Singleton();
}
}
return _instance;
}
Mã ở trên không phải là chủ đề an toàn. Mặc dù nó kiểm tra giá trị của thể hiện một lần nữa trong khối được đồng bộ hóa (vì lý do hiệu năng), trình biên dịch JIT có thể sắp xếp lại mã byte theo cách mà tham chiếu đến thể hiện được đặt trước khi hàm tạo kết thúc thực thi. Điều này có nghĩa là phương thức getInstance () trả về một đối tượng có thể chưa được khởi tạo hoàn toàn. Để làm cho luồng mã an toàn, từ khóa có thể được sử dụng kể từ Java 5 cho biến thể hiện. Các biến được đánh dấu là dễ bay hơi chỉ hiển thị cho các luồng khác khi hàm tạo của đối tượng đã hoàn thành việc thực hiện hoàn toàn.
Nguồn
volatile
sử dụng trong Java :
Các trình vòng lặp không nhanh thường được triển khai bằng cách sử dụng bộ volatile
đếm trên đối tượng danh sách.
- Khi danh sách được cập nhật, bộ đếm được tăng lên.
- Khi một
Iterator
được tạo, giá trị hiện tại của bộ đếm được nhúng trong Iterator
đối tượng.
- Khi một
Iterator
thao tác được thực hiện, phương thức so sánh hai giá trị bộ đếm và ném a ConcurrentModificationException
nếu chúng khác nhau.
Việc thực hiện các trình vòng lặp không an toàn thường có trọng lượng nhẹ. Họ thường dựa vào các thuộc tính của cấu trúc dữ liệu của việc thực hiện danh sách cụ thể. Không có mô hình chung.
volatile
đi kèm với Mô hình bộ nhớ Java mới được định nghĩa trong JSR 133: rằng khi một luồng đọc mộtvolatile
biến, nó không chỉ thấy giá trị được ghi bởi nó bởi một luồng khác, mà còn tất cả các ghi khác cho các biến khác có thể nhìn thấy trong chủ đề khác tại thời điểmvolatile
viết. Xem câu trả lời này và tài liệu tham khảo này .