2011-06-23 41 views
12

Bất cứ ai có thể cho tôi biết lớp học này có an toàn hay không?Đồng thời Java: là trường cuối cùng (khởi tạo trong hàm dựng) thread-safe?

class Foo { 

    private final Map<String,String> aMap; 

    public Foo() { 
     aMap = new HashMap<String, String>(); 
     aMap.put("1", "a"); 
     aMap.put("2", "b"); 
     aMap.put("3", "c"); 
    } 

    public String get(String key) { 
     return aMap.get(key); 
    } 

} 

Chỉnh sửa: Lỗi của tôi không làm rõ câu hỏi. Theo số JMM FAQ:

Cần đảm bảo đảm bảo an toàn khởi tạo mới. Nếu một đối tượng được xây dựng đúng (có nghĩa là các tham chiếu đến nó không thoát trong khi xây dựng), thì tất cả các luồng xem tham chiếu đến đối tượng đó cũng sẽ thấy các giá trị cho các trường cuối cùng được thiết lập trong hàm khởi tạo mà không cần đồng bộ hóa.

Điều này khiến tôi nhầm lẫn rằng tập hợp thành aMap là aMap = new HashMap<String, String>();. Vì vậy, các chủ đề khác có thể xem các số này

aMap.put("1", "a"); 
aMap.put("2", "b"); 
aMap.put("3", "c"); 

hay không?

Edit: Tôi tìm thấy question này mà chính xác đóng cửa để câu hỏi của tôi

+1

Đó là, nhưng sự hiện diện của từ khóa cuối cùng hoàn toàn không có gì để làm với điều đó, do đó, có vẻ như vẫn còn một số nhầm lẫn. Nếu bạn giải thích những gì bạn đang mong đợi đang xảy ra, chúng tôi có thể giúp đỡ nhiều hơn. – Affe

+2

Ah-ha! Xem những gì bạn đang yêu cầu ngay bây giờ. Rõ ràng là tôi dành quá nhiều thời gian trong vùng đất của những hạt cà phê đơn thuần được quản lý. Có, nếu bạn đọc tất cả phần 17.5 của đặc tả ngôn ngữ java (bài viết mà bạn đã liên kết tóm tắt), nó đề cập rằng các đối tượng được tham chiếu bởi các trường cuối cùng cũng được đảm bảo cập nhật khi kết thúc xây dựng. – Affe

Trả lời

14

Như đã chỉ ra nó hoàn toàn thread-safe và final là quan trọng ở đây do ảnh hưởng tầm nhìn bộ nhớ của nó.

Sự hiện diện của final đảm bảo rằng các chuỗi khác sẽ thấy các giá trị trong bản đồ sau khi hàm tạo hoàn tất mà không có bất kỳ đồng bộ hóa bên ngoài nào.Nếu không có final nó không thể được đảm bảo trong mọi trường hợp, và bạn sẽ cần phải sử dụng thành ngữ xuất bản an toàn khi đưa ra đối tượng mới được xây dựng sẵn cho chủ đề khác, cụ thể là (từ Java Concurrency in Practice):

  • Khởi tạo một tham chiếu đối tượng từ bộ khởi tạo tĩnh;
  • Lưu trữ tham chiếu đến nó vào trường dễ bay hơi hoặc AtomicReference;
  • Lưu trữ tham chiếu đến trường đó vào trường cuối cùng của đối tượng được tạo đúng; hoặc
  • Lưu trữ tham chiếu đến trường đó vào trường được khóa cẩn thận.
1

Có nó là, cung cấp này là toàn bộ định nghĩa lớp và không phải là một đoạn mã đó.

Thực tế chính là không có cách nào nội dung của aMap có thể được sửa đổi sau xây dựng.

+1

Thực ra, từ khóa cuối cùng là rất quan trọng. Xem http://jeremymanson.blogspot.com/2008/04/immutability-in-java.html để có giải thích tốt về lý do tại sao. –

6

Vâng. Không có cách nào để sửa đổi các tham chiếu aMap chính nó, hoặc thêm vào bản đồ sau khi các nhà xây dựng (barring phản ánh).

Nếu bạn phơi bày aMap thì sẽ không có, bởi vì hai chủ đề sau đó có thể sửa đổi bản đồ cùng một lúc.

Bạn có thể cải thiện lớp học của mình bằng cách thực hiện aMap không thể sửa đổi qua Collections.unmodifiableCollection hoặc Collections.unmodifiableMap.

0

Vì hiện tại nó phải là an toàn chỉ. Tuy nhiên, nếu bạn thêm các phương thức khác để sửa đổi hashmap thì không.

1

Lớp này không có vấn đề đồng thời khiến bạn chỉ hiển thị phương thức get. Nếu bạn thêm một số phương pháp sửa đổi bản đồ, bạn phải đánh dấu phương thức này là synchronized.

2

Guava có các lớp bất biến để làm việc này dễ dàng hơn và đảm bảo bất di bất dịch:

private final ImmutableMap<String, String> aMap = ImmutableMap.of(
    "1", "a", 
    "2", "b", 
    "3", "c"); 
0

Tôi không nghĩ đoạn mã trên là an toàn. Dòng duy nhất mà là an toàn code đang

aMap = new HashMap<String, String>(); 

Theo ví dụ được đưa ra trong http://www.cs.umd.edu/~pugh/java/memoryModel/jsr-133-faq.html,

class FinalFieldExample { 
    final int x; 
    int y; 
    static FinalFieldExample f; 
    public FinalFieldExample() { 
     x = 3; 
     y = 4; 
    } 

    static void writer() { 
     f = new FinalFieldExample(); 
    } 

    static void reader() { 
    if (f != null) { 
     int i = f.x; // x is guaranteed to be 3 
     int j = f.y; // y can have any value 
    } 
    } 
} 

Điều này có nghĩa rằng một khi các lĩnh vực chính thức được khởi động không có chủ đề an toàn được đảm bảo. Vì chỉ có phép gán tham chiếu được đảm bảo là luồng an toàn và chính đối tượng có thể được thay đổi theo ví dụ của bạn. tuyên bố sau có thể không được chủ đề an toàn

aMap.put("1", "a"); 
aMap.put("2", "b"); 
aMap.put("3", "c"); 

EDIT xấu tôi thấy những ý kiến ​​dưới đây mã sau

Khả năng nhìn thấy giá trị xây dựng một cách chính xác cho lĩnh vực này là tốt đẹp, nhưng nếu lĩnh vực này chính nó là một tham chiếu, sau đó bạn cũng muốn mã của bạn để xem các giá trị cập nhật cho đối tượng (hoặc mảng) mà nó trỏ đến. Nếu trường của bạn là trường cuối cùng, điều này cũng được đảm bảo. Vì vậy, bạn có thể có một con trỏ cuối cùng đến một mảng và không phải lo lắng về các chủ đề khác nhìn thấy các giá trị chính xác cho tham chiếu mảng, nhưng các giá trị không chính xác cho các nội dung của mảng. Một lần nữa, bằng "đúng" ở đây, chúng tôi có nghĩa là "cập nhật khi kết thúc của hàm tạo của đối tượng", không phải "giá trị mới nhất có sẵn".

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