AtomicInteger lazySet so với set


116

Sự khác biệt giữa các lazySetsetcác phương pháp là AtomicIntegergì? Các tài liệu không có nhiều điều để nói về lazySet:

Cuối cùng đặt thành giá trị đã cho.

Có vẻ như giá trị được lưu trữ sẽ không được đặt ngay lập tức thành giá trị mong muốn mà thay vào đó sẽ được lên lịch đặt vào một thời điểm nào đó trong tương lai. Nhưng, công dụng thực tế của phương pháp này là gì? Có ví dụ nào không?

Câu trả lời:


114

Được trích dẫn trực tiếp từ "JDK-6275329: Thêm phương thức lazySet vào các lớp nguyên tử" :

Có thể là phần tiếp theo JSR166 nhỏ cuối cùng cho Mustang, chúng tôi đã thêm phương thức "lazySet" vào các lớp Atomic (AtomicInteger, AtomicReference, v.v.). Đây là một phương pháp thích hợp đôi khi hữu ích khi tinh chỉnh mã bằng cách sử dụng cấu trúc dữ liệu không chặn. Ngữ nghĩa là việc ghi được đảm bảo không được sắp xếp lại với bất kỳ lần ghi nào trước đó, nhưng có thể được sắp xếp lại thứ tự với các hoạt động tiếp theo (hoặc tương đương, có thể không hiển thị với các luồng khác cho đến khi một số hành động ghi hoặc đồng bộ hóa dễ bay hơi khác xảy ra).

Trường hợp sử dụng chính là để loại bỏ các trường của các nút trong cấu trúc dữ liệu không chặn chỉ nhằm mục đích tránh lưu giữ rác lâu dài; nó áp dụng khi nó vô hại nếu các luồng khác nhìn thấy các giá trị không phải null trong một thời gian, nhưng bạn muốn đảm bảo rằng các cấu trúc cuối cùng là GCable. Trong những trường hợp như vậy, bạn có thể có được hiệu suất tốt hơn bằng cách tránh các chi phí của việc ghi biến động null. Có một số trường hợp sử dụng khác dọc theo những dòng này cho các nguyên tử không dựa trên tham chiếu, vì vậy phương pháp này được hỗ trợ trên tất cả các lớp AtomicX.

Đối với những người thích nghĩ về các hoạt động này theo rào cản cấp độ máy trên các bộ đa xử lý thông thường, lazySet cung cấp rào cản trước đó của cửa hàng - cửa hàng (có thể là không hoặc rất rẻ trên các nền tảng hiện tại), nhưng không có rào cản về tải tại cửa hàng (thường là phần đắt giá của một bài viết dễ bay hơi).


14
Ai đó có thể làm cho phần còn lại của chúng ta câm lặng? :(
Gaurav

14
Lazy là phiên bản không thay đổi (ví dụ: sự thay đổi trạng thái không được đảm bảo hiển thị cho tất cả các luồng có Atomic*phạm vi trong).
ngáp vào

63
Điều tôi không hiểu là tại sao javadoc lại kém về nó.
Felipe

8
Tôi chắc rằng cuối cùng họ sẽ bắt đầu thay đổi nó. Bùm bùm.
MMJZ

3
dành cho những người muốn biết thêm về hàng rào cửa hàng / tải và tại sao hàng rào cửa hàng lại rẻ hơn hàng rào tải cửa hàng. Đây là một bài viết dễ hiểu về nó. Mechanical-sympathy.blogspot.com/2011/07/…
Kin Cheung

15

lazySet có thể được sử dụng cho giao tiếp giữa các luồng rmw, vì xchg là nguyên tử, đối với khả năng hiển thị, khi quy trình luồng người viết sửa đổi vị trí dòng bộ nhớ cache, bộ xử lý của luồng trình đọc sẽ nhìn thấy nó ở lần đọc tiếp theo, vì giao thức kết hợp bộ nhớ cache của cpu intel sẽ đảm bảo LazySet hoạt động, nhưng dòng bộ nhớ cache sẽ được cập nhật ở lần đọc tiếp theo, một lần nữa, CPU phải đủ hiện đại.

http://sc.tamu.edu/systems/eos/nehalem.pdf Đối với Nehalem là nền tảng đa bộ xử lý, bộ xử lý có khả năng "rình mò" (nghe trộm) bus địa chỉ để truy cập của bộ xử lý khác vào bộ nhớ hệ thống và vào bộ nhớ đệm bên trong của họ. Họ sử dụng khả năng rình mò này để giữ cho bộ nhớ đệm bên trong của chúng phù hợp với cả bộ nhớ hệ thống và bộ nhớ đệm trong các bộ xử lý được kết nối với nhau khác. Nếu thông qua tính năng theo dõi, một bộ xử lý phát hiện ra rằng một bộ xử lý khác dự định ghi vào một vị trí bộ nhớ mà nó hiện đã lưu trong bộ nhớ đệm ở trạng thái Chia sẻ, bộ xử lý theo dõi sẽ làm mất hiệu lực khối bộ nhớ cache của nó buộc nó thực hiện lấp đầy dòng bộ nhớ đệm vào lần tiếp theo nó truy cập vào cùng một vị trí bộ nhớ .

oracle hotspot jdk cho kiến ​​trúc cpu x86->

lazySet == secure.putOrderedLong == xchg rw (lệnh asm đóng vai trò như một rào cản mềm có giá 20 chu kỳ trên cpu intel nehelem)

trên x86 (x86_64) một rào cản như vậy có hiệu suất rẻ hơn nhiều so với dễ bay hơi hoặc AtomicLong getAndAdd,

Trong kịch bản một nhà sản xuất, một hàng đợi của người tiêu dùng, rào cản mềm xchg có thể buộc dòng mã trước lazySet (chuỗi + 1) cho chuỗi nhà sản xuất xảy ra TRƯỚC bất kỳ mã chuỗi người tiêu dùng nào sẽ sử dụng (hoạt động trên) dữ liệu mới, tất nhiên luồng người tiêu dùng sẽ cần phải kiểm tra nguyên tử xem trình tự nhà sản xuất đã được tăng lên bởi chính xác chưa bằng cách sử dụng CompareAndSet (chuỗi, chuỗi + 1).

Tôi đã truy tìm mã nguồn Hotspot để tìm ánh xạ chính xác của lazySet tới mã cpp: http://hg.openjdk.java.net/jdk7/jdk7/hotspot/file/9b0ca45cd756/src/share/vm/prims/unsafe. cpp Unsafe_setOrderedLong -> Định nghĩa SET_FIELD_VOLATILE -> OrderAccess: release_store_fence. Đối với x86_64, OrderAccess: release_store_fence được định nghĩa là sử dụng lệnh xchg.

Bạn có thể xem cách nó được định nghĩa chính xác trong jdk7 (doug lea đang làm việc trên một số nội dung mới cho JDK 8): http://hg.openjdk.java.net/jdk7/jdk7/hotspot/file/4fc084dac61e/src/os_cpu/ linux_x86 / vm / orderAccess_linux_x86.inline.hpp

bạn cũng có thể sử dụng hdis để tháo rời tập hợp mã lazySet đang hoạt động.

Có một câu hỏi liên quan khác: Chúng ta có cần mfence khi sử dụng xchg


5
Thật khó để hiểu những gì bạn đang nhận được ở đây. Bạn có thể vui lòng làm rõ quan điểm của bạn?
Paul Bellora

3
"lazySet == secure.putOrderedLong == xchg rw (lệnh asm đóng vai trò như một rào cản mềm có giá 20 chu kỳ trên cpu intel nehelem) trên x86 (x86_64) một rào cản như vậy có hiệu suất rẻ hơn nhiều so với dễ bay hơi hoặc AtomicLong getAndAdd" -> Điều này không đúng với hiểu biết của tôi. lazySet / putOrdered là một MOV tới một địa chỉ, đó là lý do tại sao sách dạy nấu ăn JMM mô tả nó như là một no-op trên x86.
Nitsan Wakart,

11

Có thể tìm thấy một cuộc thảo luận rộng hơn về nguồn gốc và tiện ích của lazySet và putOrdered cơ bản tại đây: http://psy-lob-saw.blogspot.co.uk/2012/12/atomiclazyset-is-performance-win-for.html

Tóm lại: lazySet là một văn bản biến động yếu theo nghĩa là nó hoạt động như một cửa hàng lưu trữ chứ không phải hàng rào tải cửa hàng. Điều này dẫn đến việc lazySet đang được JIT biên dịch thành một lệnh MOV mà trình biên dịch không thể sắp xếp lại thứ tự sau đó là lệnh đắt hơn đáng kể được sử dụng cho một tập hợp dễ bay hơi.

Khi đọc giá trị, bạn luôn luôn thực hiện một lần đọc biến động (với Atomic * .get () trong mọi trường hợp).

lazySet cung cấp cho một người viết một cơ chế ghi biến động nhất quán, tức là việc một người viết sử dụng lazySet để tăng một bộ đếm là hoàn toàn hợp pháp, nhiều luồng tăng cùng một bộ đếm sẽ phải giải quyết các lần ghi cạnh tranh bằng CAS, đó chính xác là những gì xảy ra dưới bìa của Atomic * cho incAndGet.


chính xác, tại sao chúng ta không thể nói rằng đây là một StoreStorerào cản đơn giản , nhưng không phải là một StoreLoad?
Eugene

8

Từ tóm tắt gói nguyên tử đồng thời

lazySet có các hiệu ứng bộ nhớ của việc ghi (gán) một biến dễ bay hơi ngoại trừ việc nó cho phép sắp xếp lại thứ tự với các hành động bộ nhớ tiếp theo (nhưng không phải trước đó) mà bản thân nó không áp đặt các ràng buộc sắp xếp lại với việc ghi không bay hơi thông thường. Trong số các ngữ cảnh sử dụng khác, lazySet có thể áp dụng khi vô hiệu hóa, vì mục đích thu thập rác, một tham chiếu không bao giờ được truy cập lại.

Nếu bạn tò mò về lazySet thì bạn cũng nợ chính mình những lời giải thích khác

Các hiệu ứng bộ nhớ để truy cập và cập nhật nguyên tử thường tuân theo các quy tắc đối với chất bay hơi, như đã nêu trong phần 17.4 của Đặc tả ngôn ngữ Java ™.

get có tác dụng bộ nhớ khi đọc một biến dễ bay hơi.

set có các hiệu ứng bộ nhớ ghi (gán) một biến biến động.

lazySet có các hiệu ứng bộ nhớ của việc ghi (gán) một biến dễ bay hơi ngoại trừ việc nó cho phép sắp xếp lại thứ tự với các hành động bộ nhớ tiếp theo (nhưng không phải trước đó) mà bản thân nó không áp đặt các ràng buộc sắp xếp lại với việc ghi không bay hơi thông thường. Trong số các ngữ cảnh sử dụng khác, lazySet có thể áp dụng khi vô hiệu hóa, vì mục đích thu thập rác, một tham chiếu không bao giờ được truy cập lại.

yếuCompareAndSet đọc nguyên tử và ghi có điều kiện một biến nhưng không tạo ra bất kỳ chuỗi nào xảy ra trước khi xảy ra, vì vậy không đảm bảo cho việc đọc và ghi trước đó hoặc sau đó của bất kỳ biến nào khác ngoài mục tiêu của yếuCompareAndSet.

CompareAndSet và tất cả các hoạt động đọc và cập nhật khác như getAndIncrement có các hiệu ứng bộ nhớ của cả việc đọc và ghi các biến biến động.


4

Đây là sự hiểu biết của tôi, hãy sửa cho tôi nếu tôi sai: Bạn có thể nghĩ lazySet()là "bán" dễ bay hơi: về cơ bản nó là một biến không thay đổi theo cách đọc của các chủ đề khác, tức là giá trị được đặt bởi lazySet có thể không hiển thị với người khác chủ đề. Nhưng nó trở nên dễ bay hơi khi một thao tác ghi khác xảy ra (có thể là từ các luồng khác). Tác động duy nhất của lazySet mà tôi có thể tưởng tượng là compareAndSet. Vì vậy, nếu bạn sử dụng lazySet(), get()từ các luồng khác có thể vẫn nhận giá trị cũ, nhưng compareAndSet()sẽ luôn có giá trị mới vì nó là hoạt động ghi.


1
bạn không có nghĩa là compareAndSet?
Dave Moten

2

Re: cố gắng làm nó ngu ngốc -

Bạn có thể coi đây là một cách để xử lý một trường dễ bay hơi như thể nó không dễ bay hơi cho một cửa hàng cụ thể (ví dụ: ref = null;) hoạt động.

Điều đó không hoàn toàn chính xác, nhưng đủ để bạn có thể đưa ra quyết định giữa "OK, tôi thực sự không quan tâm" và "Hmm, hãy để tôi suy nghĩ về điều đó một chút".

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.