[Java Concurrency] Từ khóa volatile trong Java

Từ khóa volatile trong Java được sử dụng để đánh dấu một biến Java “được lưu trữ trong bộ nhớ chính“. Nói một cách chính xác hơn nghĩa là, mỗi lần đọc một biến volatile, sẽ đọc từ main memory của máy tính chứ không phải đọc từ CPU Cache; và mỗi lần ghi vào biến volatile thì giá trị sẽ được ghi trở lại main memory ngay lập tức.

Bài viết này sẽ không viết một cách chi tiết về từ khóa volatile, mà sẽ chỉ ghi lại một số lưu ý quan trọng khi sử dụng volatile:

Từ khóa volatile đảm bảo sự thay đổi trên các biến đều được các threads khác trông thấy. Nói một cách cụ thể hơn thì, trong một ứng dụng đa luồng (multithreaded), vì lý do performance, mỗi luồng (thread) có thể tạo một bản copy các biến từ main memory vào CPU cache để thao tác với chúng. Nếu máy tính của bạn chứa nhiều CPU, mỗi luồng có thể chạy trên một CPU khác nhau. Điều đó có nghĩa là, mỗi luồng có thể sao chép các biến vào CPU cache của các CPU khác nhau.

Nếu biến không được khai báo với volatile, thì không có sự đảm bảo mỗi khi giá trị của biến được cập nhật trong CPU cache, sẽ được update lại main memory. Điều đó có nghĩa là giá trị của biến trong CPU cache sẽ khác giá trị trong main memory, và việc thay đổi giá trị của biến sẽ không được các threads khác trông thấy.

Nếu biến được khai báo với volatile, thì tất cả sự thay đổi giá trị biến sẽ được ghi lại main memory ngay lập tức. Và khi các threads khác cần thao tác với biến, sẽ đọc trực tiếp từ main memory và sẽ lấy được giá trị mới nhất của biến.

Chỉ dùng mỗi volatile thì không đủ

Trong một ứng dụng đa luồng, nếu chỉ có duy nhất một thread thực hiện việc ghi biến, còn tất cả các threads khác chỉ đọc thì việc sử dụng volatile là đủ để cho các threads đọc được giá trị mới nhất đã được ghi.

Thậm chí, ngay cả khi có nhiều thread cùng thực hiện việc ghi biến, nhưng với điều kiện là việc tạo giá trị mới của biến không phụ thuộc vào giá trị cũ của biến đó, thì việc khai báo biến với volatile vẫn đảm bảo được sự chính xác.

Nhưng việc tạo giá trị mới của biến phụ thuộc vào giá trị trước đó của biến thì biến volatile không đủ. Khoảng thời gian ngắn giữa việc đọc biến và ghi giá trị mới cho nó sẽ tạo ra một sự tương tranh. Khi mà các threads cùng đọc biến từ main memory, sau đó ghi giá trị mới cho biến và update ngược trở lại main memory, các giá trị sẽ bị ghi đè lẫn nhau.

Vậy nên sử dụng volatile thế nào?

Như đã đề cập bên trên, nếu nhiều threads cùng đọc và ghi một biến thì việc dùng từ khóa volatile là không đủ. Chúng ta phải dùng kết hợp synchronized trong trường hợp đó để đảm bảo việc đọc và ghi biến là atomic.

Vì về bản chất việc đọc và ghi biến volatile, sẽ không block các threads đọc và ghi. Vì vậy, ta phải dùng kết hợp synchronized.

Nếu bạn không thích sử dụng synchronized, thì bạn có thể dùng 1 trong các atomic data types được khai báo trong package java.util.concurrent như là AtomicLong, AtomicReference,…

Tham khảo:

http://tutorials.jenkov.com/java-concurrency/volatile.html

Advertisement

Cài đặt chế độ riêng tư