Không có ý nghĩa gì về nguyên tử của người Viking trong lập trình?


273

Trong sách Java hiệu quả, nó ghi:

Đặc tả ngôn ngữ đảm bảo rằng việc đọc hoặc viết một biến là nguyên tử trừ khi biến đó thuộc loại longhoặc double[JLS, 17.4.7].

"Nguyên tử" nghĩa là gì trong bối cảnh lập trình Java hay lập trình nói chung?


24
Một hoạt động tại một thời điểm.
Subhrajyoti Majumder

1
mỗi lần chỉ có thể thực hiện một thao tác trên biến.
kaysush


1
Tôi nghi ngờ các câu hỏi triết học thuộc về codereview.stackexchange.com
Phlip

Lưu ý rằng một số biến không theo mặc định có đọc và ghi nguyên tử, khai báo chúng là volatile longhoặc volatile doublelàm cho đọc nguyên tử và viết nguyên tử.
H2ONaCl

Câu trả lời:


372

Đây là một ví dụ, bởi vì một ví dụ thường rõ ràng hơn một lời giải thích dài. Giả sử foolà một biến của loại long. Các hoạt động sau đây không phải là một hoạt động nguyên tử:

foo = 65465498L;

Thật vậy, biến được viết bằng hai thao tác riêng biệt: một thao tác ghi 32 bit đầu tiên và biến thứ hai ghi 32 bit cuối. Điều đó có nghĩa là một luồng khác có thể đọc giá trị của foovà xem trạng thái trung gian.

Làm cho nguyên tử hoạt động bao gồm sử dụng các cơ chế đồng bộ hóa để đảm bảo rằng hoạt động được nhìn thấy, từ bất kỳ luồng nào khác, dưới dạng một nguyên tử đơn lẻ (nghĩa là không thể chia tách trong các bộ phận), hoạt động. Điều đó có nghĩa là bất kỳ luồng nào khác, một khi hoạt động được tạo thành nguyên tử, sẽ thấy giá trị của footrước khi gán hoặc sau khi gán. Nhưng không bao giờ là giá trị trung gian.

Một cách đơn giản để làm điều này là làm cho biến số biến động :

private volatile long foo;

Hoặc để đồng bộ hóa mọi quyền truy cập vào biến:

public synchronized void setFoo(long value) {
    this.foo = value;
}

public synchronized long getFoo() {
    return this.foo;
}
// no other use of foo outside of these two methods, unless also synchronized

Hoặc để thay thế nó bằng AtomicLong:

private AtomicLong foo;

75
Vì vậy, điều này giả sử rằng nó đang chạy trong hệ thống 32 bit. Nếu đó là hệ thống 64 bit thì sao? Sẽ foo = 65465498L; được nguyên tử thì sao?
Harke

46
@Harke Nếu bạn đang chạy Java 64 bit, vâng.
Jeroen

4
Điều này có áp dụng cho C # và .NET không? Nếu có, để foo có một hành vi nguyên tử, CLR phải là 64 bit?
Fabiano

5
@Fabiano Nó được áp dụng và đây là cách để đạt được nó trong .NET vì chúng tôi không có từ khóa được đồng bộ hóa như Java. stackoverflow.com/questions/541194/ trộm
Người đàn ông Muffin

2
Sau đó, giả sử luồng A gán giá trị dài sau đó trong nửa đường, luồng B cố gắng đọc nó. Nếu hoạt động A là nguyên tử, thì luồng B sẽ đợi cho đến khi nó kết thúc? Điều này có nghĩa là hoạt động nguyên tử sẽ cung cấp an toàn chủ đề ngầm?
Teoman shipahi

60

"Hoạt động nguyên tử" có nghĩa là một hoạt động dường như là tức thời từ quan điểm của tất cả các luồng khác. Bạn không cần phải lo lắng về hoạt động hoàn thành một phần khi áp dụng bảo hành.


25

Đó là một cái gì đó "dường như phần còn lại của hệ thống xảy ra tức thời" và nằm trong phân loại của tính tuyến tính trong các quy trình tính toán. Để trích dẫn bài viết liên kết đó hơn nữa:

Nguyên tử là một đảm bảo của sự cô lập từ các quá trình đồng thời. Ngoài ra, các hoạt động nguyên tử thường có định nghĩa thành công hoặc thất bại - chúng có thể thay đổi thành công trạng thái của hệ thống hoặc không có hiệu lực rõ ràng.

Vì vậy, ví dụ, trong bối cảnh của một hệ thống cơ sở dữ liệu, người ta có thể có 'cam kết nguyên tử', nghĩa là bạn có thể đẩy một thay đổi các bản cập nhật đến cơ sở dữ liệu quan hệ và tất cả những thay đổi đó sẽ được gửi hoặc không có gì trong số chúng trong trường hợp thất bại, theo cách này, dữ liệu không bị hỏng và do đó các khóa và / hoặc hàng đợi, thao tác tiếp theo sẽ là ghi hoặc đọc khác, nhưng chỉ sau khi thực tế. Trong ngữ cảnh của các biến và luồng, điều này rất giống nhau, được áp dụng cho bộ nhớ.

Trích dẫn của bạn nhấn mạnh rằng điều này không cần phải được mong đợi trong mọi trường hợp.


15

Chỉ cần tìm thấy một bài viết Nguyên tử so với hoạt động phi nguyên tử là rất hữu ích cho tôi.

"Một hoạt động trên bộ nhớ dùng chung là nguyên tử nếu nó hoàn thành trong một bước duy nhất so với các luồng khác.

Khi một cửa hàng nguyên tử được thực hiện trên bộ nhớ dùng chung, không có luồng nào khác có thể quan sát được nửa sửa đổi.

Khi tải nguyên tử được thực hiện trên một biến được chia sẻ, nó sẽ đọc toàn bộ giá trị khi nó xuất hiện tại một thời điểm duy nhất. "


14

Nếu bạn có một số luồng thực thi các phương thức m1 và m2 trong mã dưới đây:

class SomeClass {
    private int i = 0;

    public void m1() { i = 5; }
    public int m2() { return i; }
}

bạn có đảm bảo rằng bất kỳ cuộc gọi luồng nào m2cũng sẽ đọc 0 hoặc 5.

Mặt khác, với mã này (nơi idài):

class SomeClass {
    private long i = 0;

    public void m1() { i = 1234567890L; }
    public long m2() { return i; }
}

một cuộc gọi luồng m2có thể đọc 0, 1234567890L hoặc một số giá trị ngẫu nhiên khác vì câu lệnh i = 1234567890Lkhông được đảm bảo là nguyên tử cho một long(một JVM có thể ghi 32 bit đầu tiên và 32 bit cuối cùng trong hai thao tác và một luồng có thể quan sát iở giữa) .


Tại sao bạn nghĩ rằng "dài" gây ra vấn đề trong khi "int" thì không? Vui lòng xem tại đây geekswithbloss.net/BlackRmusCoder/archive/2012/08/09/iêu
onmyway133

1
@entropy các bài tập dài và kép không được đảm bảo là nguyên tử trong Java. Vì vậy, bạn có thể đọc một đoạn dài trong đó chỉ một nửa số bit đã được cập nhật sau khi gán.
assylias

0

Trong các trường đọc và viết Java của tất cả các loại trừ trường hợp dài và gấp đôi xảy ra nguyên tử và nếu trường được khai báo với bộ sửa đổi dễ bay hơi, thậm chí dài và gấp đôi được đọc và ghi nguyên tử. Đó là, chúng tôi nhận được 100% hoặc những gì đã có, hoặc những gì đã xảy ra ở đó, cũng không thể có bất kỳ kết quả trung gian nào trong các biến.

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.