2011-11-05 33 views
8

Tôi có một đoạn mã mà trông như thế này:Biến Java biến động có áp đặt mối quan hệ xảy ra trước khi nó được đọc không?

Snippet A:

class Creature { 
    private static long numCreated; 
    public Creature() { 
     synchronized (Creature.class) { 
      numCreated++; 
     } 
    } 
    public static long numCreated() { 
     return numCreated; 
    } 
} 

Từ hiểu biết của tôi, kể từ khi đọc numCreated không đồng bộ, nếu Chủ đề-A tạo ra một Creature tại 13:00 và Thread-B đọc numCreated() lúc 2 giờ chiều, numCreated() cũng có thể trả về 0 hoặc 1 (ngay cả khi Thread-A đã hoàn tất khởi tạo đối tượng lúc 1.05pm).

Vì vậy, tôi đã thêm synchronized để numCreated():

Snippet B:

class Creature { 
    private static long numCreated; 
    public Creature() { 
     synchronized (Creature.class) { 
      numCreated++; 
     } 
    } 
    public static synchronized long numCreated() { // add "synchronized" 
     return numCreated; 
    } 
} 

và tất cả là tốt, ngoại trừ việc tôi đã suy nghĩ, nếu tôi sửa đổi nó để Snippet C, là biến numCreated vẫn được đồng bộ hóa đúng cách?

Snippet C:

class Creature { 
    private static volatile long numCreated; // add "volatile" 
    public Creature() { 
     synchronized (Creature.class) { 
      numCreated++; 
     } 
    } 
    public static long numCreated() { // remove "synchronized" 
     return numCreated; 
    } 
} 

Với Snippet C, là nó đảm bảo rằng càng sớm càng Chủ đề-A hoàn thành đối tượng sáng tạo lúc 1:05 giờ chiều, cuộc gọi Thread-B để numCreated() chắc chắn sẽ trở lại 1?

PS: Tôi hiểu rằng trong một tình huống thực sự tôi có lẽ sẽ sử dụng một AtomicLong nhưng điều này là dành cho mục đích học tập

+0

lượt đọc dễ bay hơi rẻ, đây là những gì bạn cần biết. Tuy nhiên, viết không phải là giá rẻ, mặc dù. Đồng bộ hóa đòi hỏi cả một viết dễ bay hơi và CAS (so sánh và thiết lập), cộng với màn hình contended bảo lãnh cho hệ điều hành đó là động lực để khóa-miễn phí impl. – bestsss

+0

đưa ra nhận xét trước, không cố gắng chơi * thông minh hơn * bằng cách loại bỏ dễ bay hơi. – bestsss

+0

@bestsss Tôi không hiểu nhận xét cuối cùng của bạn, ý của bạn là gì khi không loại bỏ dễ bay hơi? (trong Snippet C tôi đã thêm dễ bay hơi và loại bỏ đồng bộ) – Pacerier

Trả lời

6

Xem http://download.oracle.com/javase/6/docs/api/java/util/concurrent/package-summary.html#MemoryVisibility:

Một ghi vào một lĩnh vực không ổn định xảy ra-trước mỗi đọc tiếp theo của cùng một trường. Việc ghi và đọc các trường dễ bay hơi có hiệu ứng tương tự như bộ nhớ khi vào và thoát khỏi màn hình, nhưng làm không yêu cầu khóa loại trừ lẫn nhau.

Vì vậy, câu trả lời là có. Việc viết của các biến động trong constructor xảy ra trước khi đọc của biến động trong numCreated(). Và vì sự gia tăng không nguyên tử vẫn được thực hiện trong một khối đồng bộ, việc đồng bộ hóa là không sao (sự gia tăng không phải là nguyên tử, nhưng việc viết lâu dài dễ bay hơi là).

+0

Cảm ơn bạn đã giúp đỡ = D Btw bạn có thể nhận xét về hiệu suất của việc sử dụng dễ bay hơi (Snippet C) so với hiệu suất của việc sử dụng đồng bộ để đọc (Snippet B)? Nói cách khác, bạn thích B hay C hơn? – Pacerier

+0

Tôi thích AtomicLong hơn, vì nó giúp đồng bộ dễ dàng hơn và rõ ràng hơn. Không có ATomicLong, tôi thích đoạn mã B vì nó dễ hiểu hơn và không kết hợp hai chiến lược đồng bộ hóa. Sự khác biệt về hiệu suất sẽ không đáng kể trong 99% các trường hợp. Đa luồng là rất rất khó, thường không được hầu hết các nhà phát triển tiếp cận và không nổi tiếng. Do đó, tôi sẽ quan tâm đến khả năng đọc và chính xác trước khi quan tâm đến hiệu suất. –

+0

Hiệu suất của cả hai đều tương tự nhau kể từ khi sửa sang mô hình bộ nhớ java (phiên bản?) –

Các vấn đề liên quan