Đối với ví dụ cụ thể của bạn: nếu không được khai báo là biến JVM của máy chủ có thể đưa keepRunning
biến ra khỏi vòng lặp vì nó không được sửa đổi trong vòng lặp (biến nó thành một vòng lặp vô hạn), nhưng JVM của máy khách thì không. Đó là lý do tại sao bạn thấy kết quả khác nhau.
Giải thích chung về các biến số biến động như sau:
Khi một trường được khai báo volatile
, trình biên dịch và thời gian chạy được thông báo rằng biến này được chia sẻ và các hoạt động trên nó không được sắp xếp lại thứ tự với các hoạt động bộ nhớ khác. Các biến dễ bay hơi không được lưu trong bộ nhớ đệm trong thanh ghi hoặc trong bộ nhớ đệm nơi chúng bị ẩn khỏi các bộ xử lý khác, vì vậy việc đọc một biến dễ thay đổi luôn trả về lần ghi gần đây nhất của bất kỳ luồng nào .
Hiệu ứng khả năng hiển thị của các biến số dễ bay hơi vượt ra ngoài giá trị của chính biến số dễ bay hơi. Khi luồng A ghi vào một biến dễ thay đổi và sau đó luồng B đọc cùng một biến đó, các giá trị của tất cả các biến hiển thị cho A trước khi ghi vào biến dễ bay sẽ hiển thị cho B sau khi đọc biến dễ bay.
Việc sử dụng phổ biến nhất cho các biến biến động là cờ hoàn thành, gián đoạn hoặc trạng thái:
volatile boolean flag;
while (!flag) {
}
Các biến dễ bay hơi có thể được sử dụng cho các loại thông tin trạng thái khác, nhưng cần phải cẩn thận hơn khi thực hiện điều này. Ví dụ, ngữ nghĩa của biến không đủ mạnh để làm cho hoạt động tăng dần ( count++
) trở thành nguyên tử, trừ khi bạn có thể đảm bảo rằng biến chỉ được viết từ một luồng duy nhất.
Khóa có thể đảm bảo cả khả năng hiển thị và tính nguyên tử; các biến biến động chỉ có thể đảm bảo khả năng hiển thị.
Bạn chỉ có thể sử dụng các biến biến động khi đáp ứng tất cả các tiêu chí sau:
- Việc ghi vào biến không phụ thuộc vào giá trị hiện tại của nó hoặc bạn có thể đảm bảo rằng chỉ một luồng duy nhất cập nhật giá trị;
- Biến không tham gia bất biến với các biến trạng thái khác; và
- Không cần khóa vì bất kỳ lý do nào khác trong khi biến đang được truy cập.
Mẹo gỡ lỗi : hãy đảm bảo luôn chỉ định -server
công tắc dòng lệnh JVM khi gọi JVM, ngay cả khi phát triển và thử nghiệm. Máy chủ JVM thực hiện tối ưu hóa nhiều hơn JVM máy khách, chẳng hạn như nâng các biến ra khỏi vòng lặp mà không được sửa đổi trong vòng lặp; mã có thể hoạt động trong môi trường phát triển (máy khách JVM) có thể bị hỏng trong môi trường triển khai (máy chủ JVM).
Đây là một đoạn trích từ "Java Concurrency in Practice" , cuốn sách hay nhất mà bạn có thể tìm thấy về chủ đề này.