Volatile vs Atomic

Hi mọi người, vừa đây mình có đọc được một bài post trên facebook nói về Volatile với Atomic.

Sau khi đọc bài viết thì mình thấy tác giả đang làm sai đi bản chất của Volatile.
Theo như docs nói thì volatile hiệu quả vì nó không lock thread mà thay vào đó là khiến compiler bắt buộc các thread đọc value từ biến.

Tuy nhiên tác giả lại lấy ví dụ:

Điều này mình thấy làm sai đi hoàn toàn cái ý nghĩa của volatile. Tuy nhiên do người này đã đi làm lâu năm và một số người không phản biện được mình mà chỉ đè mình bằng lươngsố năm kinh nghiệm.

Vậy nên mạo muội hỏi mọi người xem ví dụ của tác giả kia có làm sai bản chất của Volatile không, và suy nghĩ của mình có sai ở đâu không. Cảm ơn ạ :smiley:

8 Likes

Lâu rồi mình mới like một topic, good job.

3 Likes

Mình thấy code (trên hình) không có gì sai cả, nếu hiểu về volatile thì có thể áp dụng vào để hiểu được.

Theo mình cái sai là ở bài viết. Định nghĩa bậy bạ, rồi quăng thêm code ví dụ, rồi lại từ code ví dụ để suy ngược ra định nghĩa. Nhưng do nhìn code đoán bừa nên thành ra viết sai bét nhè.

Trong khi đó nếu hiểu được volatile là gì thì dễ dàng áp dụng vào để hiểu code trên được.

Cả bài có mỗi đoạn này thấy ổn.

4 Likes

Về phần code trên hình tại sao mình nói sai bản chất. Mình thấy code trên hình đang sử dụng lock để synchronize khác hoàn toàn so với việc truy cập tuần tự của atomic.

Còn ở đoạn kia thì tác giả mới chỉ nói 1 nửa vế, đúng là các thread sẽ có các caching value riêng nhưng sau khi update nó sẽ lại đồng bộ với main memory. (Dẫn chứng)

4 Likes

A post was merged into an existing topic: Topic lưu trữ các post off-topic - version 3

Bạn nhận ra đúng chỗ có vấn đề rồi đó. Volatile không phải là lock. Và cái ví dụ volatile nó cũng sai với định nghĩa của volatile (bỏ qua vấn đề compiler features)

6 Likes

Blog sai hẳn rồi.
Volatile nó đảm bảo gia trị change được ghi ngay vào main memory để tất cả các thread điều có thể thấy được(visible of change). Tất nhiên việc này cũng ko đảm bảo sync value hoàn toàn giữa các thread.

Tìm hiểu thêm về Memory Barrier

Code trên là sync 100% rồi, volatile không phải sync.

6 Likes

Cậu gần đúng rồi đó.
Volatile không block thread, mà tất cả các thread sẽ bị buộc đọc và ghi giá trị từ main memory, chứ không phải từ CPU cache (không phải đọc từ biến nha :smile:)

Và vì vậy, hẳn nhiên những giải thích về volatile, cũng như phần code demo, đều sai cả :smile:
Định nghĩa volatile trích dẫn từ blog:

Là một dạng biến thông báo, nghĩa là nó được dùng để các thread thông báo cho nhau rằng biến có sự thay đổi giá trị, tuy nhiên nó không đảm bảo được là giá trị của biến sẽ được đồng bộ.

Trích dẫn về cơ chế hoạt động của volatile từ blog:

Mặc dù chưa đọc code của trình dịch và jvm, nhưng mình đoán cơ chế hoạt động của volatile sẽ kiểu thế này:
1. Trạng thái ban đầu được set là chưa thay đổi
2. Khi có một luồng gọi đến, nó sẽ kiểm tra xem giá trị có sự thay đổi nào không, nếu không thì block cái luồng gọi lại và chờ đợi cho đến khi có sự thay đổi
3. Khi có 1 luồng thay đổi nó sẽ thông báo đến các luồng đang chờ hãy thức dậy, vừa có sự thay đổi giá trị đó
4. Các luồng đang bị block sẽ thức dậy và thực thi tiếp

“Đoán” (assumption) mà không chứng minh giả thiết là bad practice trong ngành này.

Don’t assume it. Prove it.

Phần “khi nào sử dụng volatile” cũng sai, do việc hiểu sai bản chất kể trên.

Bởi vì volatile không đảm bảo được việc đồng bộ giá trị nên mình cũng chủ yếu dùng để làm thông báo, như ví dụ ở trên, mình dùng để thông báo khi việc chuẩn bị đã hoàn tất và chương trình có thể thực thi.

Tớ recommend cậu nên đọc article khác để biết về định nghĩa, lý do sử dụng, và cách sử dụng volatile. Cậu check ở mục “See also” nhé!

Không chỉ phần volatile, phần Atomic trong blog cũng có những vấn đề riêng của nó.
Trích dẫn định nghĩa Atomic từ blog.

Atomic là một phần trong bộ thư viện java.concurrent kể từ java 6, nó có khả năng thông báo vừa đảm bảo giá trị sẽ được đồng bộ, vì bên trong nó sử dụng kiểu private volatile int value; . Nên bạn có thể yên tâm sử dụng để thay thế cho volatile nếu muốn.

  • Atomic không có mục đích sử dụng để thay thế volatile. Nó sử dụng volatile như một phương tiện để đạt mục đích của nó, đảm bảo giá trị nó wrap là atomic.
  • Atomic có sử dụng volatile để đảm bảo giá trị được đọc từ main memory, tuy nhiên, lý do class này có thể được sử dụng trong multi thread context một cách hiệu quả, là do sử dụng sun.misc.Unsafe (ở java 8-. Sang Java 9+, sun.misc.Unsafe đã được refactor lại thành jdk.internal.misc.Unsafe). Unsafe class được sử dụng để synchronize, nhưng nhanh hơn so với sử dụng từ khóa synchronized.
    Đó là lý do atomic thread-safe.

See also:

6 Likes

Xin cảm ơn mọi người nhiều ạ. Qua đây mình cũng thấy được là không phải cứ đi làm lâu năm thì luôn đúng, chúng ta nên cần đi sâu vào bản chất và không ngừng tìm hiểu.

3 Likes

Mình vẫn giữ quan điểm code trong hình vẫn dùng để hiểu volatile được.
Nếu thuộc tính changed có volatile thì các thread get sẽ nhận biết được ngay, và break được while.

2 Likes

Đúng là điều này cho kết quả tương tự như kì vọng

Nhưng mình hoàn toàn đồng ý với anh @library đã nói

“Đoán” (assumption) mà không chứng minh giả thiết là bad practice trong ngành này.

Don’t assume it. Prove it.

Sự hiểu lơ mơ rồi thừa nhận cái giả định là cực kì nguy hiểm. Khi đã tìm hiểu về khái niệm thì chỉ có đúng hoặc sai, không thể mang cái na ná ra để mô tả. Bản chất của lock và volatile là khác nhau.

6 Likes

Nó sai vì vốn dĩ volatile không phải là lock. Nó cũng không phải kiểu listenner notify như thế. Như ví dụ thì thread sẽ block nếu read mà không có ai write. volatile không block ai khi read cả.
Nếu cần 1 ví dụ cho volatile có thể code ra được thì cách đúng nhất chính là phải mô phỏng lại các tầng cache trong bộ nhớ. 1 mô hình mà nhiều Dev vẫn đang dùng hàng ngày chính là load dữ liệu cache vào RAM sẵn, định kì reload lại từ disk, nếu dữ liệu là volatile thì luôn read write trực tiếp ra disk

4 Likes

Hình như tới đây thì hơi sai sai rồi.

volatile nghĩa là bốc hơi, nếu dùng nó với ngữ cảnh memory thì dùng để chỉ RAM. https://en.wikipedia.org/wiki/Volatile_memory
Ở đây khai báo volatile là mong muốn biến không bị optimize sang register hay cache, mà buộc phải đặt và truy xuất ở RAM. https://www.javatpoint.com/volatile-keyword-in-java

Tương tự Atomic nghĩa là nguyên tử (nhỏ nhất), nghĩa là mọi operation trên atomic đều phải là nhỏ nhất, không thể bị chia nhỏ nữa, nhằm đảm bảo không bị chen ngang bởi process/thread khác.

Người ta đặt tên từ khóa mong muốn xác với nghĩa mà nó biểu thị nhất.

4 Likes

@nitro2 Theo bạn thì nó sai chỗ nào vậy bạn. Đây là ví dụ tường minh nhất mà 1 dev bình thường có thể implement mô phỏng lại cách mà volatile hoạt động.
Vì volatile là 1 từ khóa sẽ được compiler xử lí do đó rất khó để viết 1 đoạn code mô phỏng chính xác cách mà CPU hoạt động với 1 biến volatile ở cấp độ ngôn ngữ lập trình bậc cao. Thay vì thế mình dùng 1 ví dụ mô tả gần gũi hơn là chiến lược cache dữ liệu mà nhiều dev lập trình bậc cao có thể hiểu được.

1 Like

Thank bro for supporting. Có đoạn này mình chưa thật sự hiểu lắm, bro có thể giải thích kĩ hơn được không. Trước giờ mình cứ nghĩ biến nó chỉ được ghi ra CPU register hoặc RAM, mình đang hiểu cái mà bro đang nói là cơ chế swap in - swap out của OS đúng không :smiley:

@qloved Ở chỗ volatile trên RAM, mà bạn lại hướng ví dụ qua disk, gây hiểu nhầm về bản chất của volatile . Theo mình thấy nếu muốn lấy ví dụ tương tự thì nên lấy thoát ly hoàn toàn khỏi ngữ cảnh để không lặp lại sai lầm của ví dụ về class Volatile<T> của tác giả đưa ra mà bị mọi người bash.
Mặt khác, nếu lấy ví dụ để chỉ rõ vấn đề thì mình thấy lấy ví dụ như của @nguyenhuuca trực tiếp và trực quan hơn.

4 Likes

Ví dụ như @nguyenhuuca là đúng. Nhưng có 1 vấn đề là không thể implement lại được bằng các nnlt bậc cao.
Vấn đề mà volatile giải quyết chính là vấn đề đồng bộ giữa bộ nhớ chính và bộ nhớ cục bộ. Do đó để lấy 1 ví dụ bằng code mô phỏng lại thì chỉ có thể bằng cách mô phỏng lại 1 hệ thống có nhiều tầng bộ nhớ (cache là 1 ví dụ tốt và gần gũi với nhiều Dev hơn là ở mức vlx). Bạn không nên bó buộc bộ nhớ ở đây chỉ có nghĩa là RAM

2 Likes

Mình nghĩ topic chính của bài viết là “Volatile và Atomic”, chứ không phải là “vấn đề đồng bộ giữa bộ nhớ chính và bộ nhớ cục bộ”.
Bạn @hoangthan đưa ra topic này vì cảm thấy hoang mang do tác giả bài blog đưa ra giải thích 2 từ khóa đó ngược với kiến thức của bạn đó. Các comments trên để làm sáng tỏ topic ban đầu.

Khi mình đọc ví dụ của bạn thì mình cảm thấy confused giống như bạn ở trên vậy:

Có lẽ mọi người có thể bàn luận sâu hơn về đồng bộ trên các kiểu bộ nhớ khác nhau ở một topic khác.

4 Likes

Bonus thêm bài Mechanical Sympathy, tương tác giữ phần cứng và phần mềm.

https://batnamv.medium.com/tìm-hiểu-về-khái-niệm-mechanical-sympathy-và-bộ-thư-viện-lmax-disruptor-4d553dc7fa55

Có nói về Atomic dùng cơ chế lock-free( compare and swap) và volatile(Memory Barrier).
Đọc blog nên chú ý nguồn, xem thảm khảo từ đâu.

5 Likes

Bro đỉnh quá, cái gì bro cũng biết :v

2 Likes
83% thành viên diễn đàn không hỏi bài tập, còn bạn thì sao?