2008-09-23 28 views
9

Khi một trường lớp được truy cập thông qua một phương thức getter bởi nhiều luồng, làm thế nào để bạn duy trì an toàn luồng? Từ khóa được đồng bộ có đủ không?Làm thế nào để bạn đảm bảo nhiều luồng có thể truy cập một cách an toàn một trường lớp?

là an toàn này:

public class SomeClass { 
    private int val; 

    public synchronized int getVal() { 
     return val; 
    } 

    private void setVal(int val) { 
     this.val = val; 
    } 
} 

hoặc không setter giới thiệu các biến chứng hơn nữa?

+0

tl; dr: đây không phải là an toàn chỉ. –

Trả lời

17

Nếu bạn sử dụng 'đồng bộ' trên bộ đặt ở đây, mã này là an toàn. Tuy nhiên nó có thể không đủ chi tiết; nếu bạn có 20 getters và setters và tất cả chúng đều được đồng bộ, bạn có thể đang tạo một nút cổ chai đồng bộ hóa.

Trong trường hợp cụ thể này, với một biến int, sau đó loại bỏ 'đồng bộ' và đánh dấu trường int 'dễ bay hơi' cũng sẽ đảm bảo khả năng hiển thị (mỗi chuỗi sẽ thấy giá trị mới nhất của 'val' khi gọi getter) nhưng nó có thể không được đồng bộ đủ cho nhu cầu của bạn. Ví dụ: mong đợi

int old = someThing.getVal(); 
if (old == 1) { 
    someThing.setVal(2); 
} 

để đặt val thành 2 nếu và chỉ khi nó đã là 1 không chính xác. Đối với điều này, bạn cần một khóa bên ngoài, hoặc một số phương pháp so sánh và thiết lập nguyên tử.

tôi đề nghị bạn đọc Java Concurrency in Practice bởi Brian Goetz et al, nó có độ che phủ tốt nhất của cấu trúc đồng thời Java.

+0

Xem câu trả lời của tôi cho cách bổ sung để so sánh và lưu trữ –

2

Từ hiểu biết của tôi, bạn nên sử dụng đồng bộ hóa trên cả phương thức getter và setter, và điều đó là đủ.

Chỉnh sửa: Dưới đây là một số link để biết thêm thông tin về đồng bộ hóa và thông tin nào không.

-3

Đồng bộ hóa tồn tại để bảo vệ chống lại nhiễu luồng và lỗi nhất quán của bộ nhớ. Bằng cách đồng bộ hóa trên getVal(), mã này đảm bảo rằng các phương thức đồng bộ khác trên SomeClass cũng không thực hiện cùng một lúc. Vì không có phương pháp đồng bộ hóa khác, nó không cung cấp nhiều giá trị. Cũng lưu ý rằng đọc và viết trên nguyên thủy có quyền truy cập nguyên tử. Điều đó có nghĩa là với lập trình cẩn thận, người ta không cần đồng bộ hóa quyền truy cập vào trường.

Đọc Sychronization.

Không thực sự chắc chắn tại sao điều này bị giảm xuống -3. Tôi chỉ đơn giản là tóm tắt những gì các hướng dẫn đồng bộ hóa từ Sun nói (cũng như kinh nghiệm của riêng tôi).

Sử dụng đơn giản truy cập biến nguyên tử là hiệu quả hơn so với truy cập vào những biến thông qua mã đồng bộ, nhưng đòi hỏi phải chăm sóc nhiều hơn bởi các lập trình viên để tránh bộ nhớ nhất quán lỗi. Cho dù nỗ lực thêm là đáng giá phụ thuộc vào kích thước và độ phức tạp của ứng dụng .

+0

"đọc và viết trên nguyên thủy có quyền truy cập nguyên tử" - không phải như vậy. Đọc và viết về lâu dài và tăng gấp đôi chỉ là nguyên tử nếu chúng được đánh dấu dễ bay hơi. –

+0

Ồ vâng, và bảo đảm chỉ áp dụng cho các biến, không áp dụng cho các mục trong một mảng kiểu nguyên thủy. –

+0

Đọc và ghi vào một trường (với sự cẩn thận đã nêu) là nguyên tử nhưng không đủ để đảm bảo việc xuất bản. Vì vậy, một ghi vào một trường int sẽ được viết một cách an toàn vào trường nhưng KHÔNG CÓ BẢO ĐẢM rằng các luồng khác sẽ thấy ghi. Trừ khi bạn sử dụng đồng bộ hoặc dễ bay hơi. –

3

Ngoài Cowan's comment, bạn có thể làm như sau cho một so sánh và lưu trữ:

synchronized(someThing) { 
    int old = someThing.getVal(); 
    if (old == 1) { 
     someThing.setVal(2); 
    } 
} 

này hoạt động vì khóa được xác định thông qua một phương pháp đồng bộ là ngầm giống như khóa của đối tượng (see java language spec) .

+0

nhưng nó hoàn toàn dựa trên nhận thức đồng thời của mã máy khách - không quá khuyến khích (-1) – xtofl

0

Đối với các đối tượng đơn giản, điều này có thể đủ. Trong hầu hết các trường hợp, bạn nên tránh từ khóa được đồng bộ hóa vì bạn có thể chạy vào một bế tắc đồng bộ hóa.

Ví dụ:

Đảm bảo rằng chỉ có một thread đọc hoặc viết cho thành viên dụ địa phương.

Đọc cuốn sách "Lập trình đồng thời trong Java (tm): Nguyên tắc và Patterns Thiết kế (Java (Addison-Wesley))", có lẽ http://java.sun.com/docs/books/tutorial/essential/concurrency/index.html cũng rất hữu ích ...

1

Nếu lớp học của bạn chỉ chứa một biến, thì một cách khác để đạt được an toàn luồng là sử dụng đối tượng AtomicInteger hiện có.

public class ThreadSafeSomeClass { 

    private final AtomicInteger value = new AtomicInteger(0); 

    public void setValue(int x){ 
     value.set(x); 
    } 

    public int getValue(){ 
     return value.get(); 
    } 

} 

Tuy nhiên, nếu bạn thêm biến phụ thuộc vào trạng thái của biến khác, thì AtomicInteger sẽ không hoạt động.

Cho phép đề xuất đọc "Java Concurrency in Practice".

+0

Bạn đang giết một con kiến ​​bằng một cái búa. Đây là một trường hợp sử dụng rất kém đối với AtomicInteger. Chỉ cần đánh dấu trường 'value' là' volatile' và bạn nhận được mã tương đương. –

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