2013-02-27 38 views
5

Bài viết này article về từ khóa "đồng bộ hóa" của Java.Các chuỗi Java "đã đồng bộ hóa"

... 
    private int foo; 
    public synchronized int getFoo() { return foo; } 
    public synchronized void setFoo(int f) { foo = f; } 

Nếu một người gọi muốn để tăng tài sản foo, đoạn code sau để làm như vậy không phải là thread-safe:

... 
    setFoo(getFoo() + 1); 

Nếu hai luồng cố gắng để tăng foo cùng một lúc, các kết quả có thể là giá trị của foo được tăng lên một hoặc hai, tùy thuộc vào thời gian.

Bây giờ, câu hỏi của tôi:

Tại sao không phải là "đồng bộ" trên setFoo() ngăn chặn việc in đậm dòng trên?

Trả lời

6

Đây là ví dụ về điều kiện cuộc đua kiểm tra sau đó.

Một kịch bản có thể xảy ra như sau:

Thread-1 getFoo() returns 0 
Thread-2 getFoo() returns 0 
Thread-2 setFoo(1) 
Thread-1 setFoo(1) 

Điều này có nghĩa rằng hai luồng đã cố gắng để tăng foo nhưng nó có tác dụng chỉ được tăng một lần.

Như các câu trả lời khác đã xác định, đồng bộ hóa tăng với khóa khối đồng bộ trên cùng một đối tượng như getFoo() và setFoo() sẽ ngăn chặn điều kiện chủng tộc này vì luồng sẽ không thể xen kẽ như trên.

6

vì bạn chắc chắn không có ai khác đang nhận foo bên cạnh bạn và không ai khác đang thiết lập lại bên cạnh bạn, nhưng bạn KHÔNG được đảm bảo không ai quản lý vào và ra (hoặc chỉ trong) giữa bạn gọi get() và bạn gọi điện thoại set()

bạn có thể nghĩ đến mã mà là hoàn toàn tương đương với điều này:

int temp = getFoo(); //safe method 
temp = temp+1; //not protected here - im not holding any locks ... 
setFoo(temp); //safe method 
4

Từ khóa synchronized trên cả hai phương pháp không làm cho nó chủ đề an toàn, bởi vì một thread có thể hãy gọi getFoo, sau đó, một chuỗi khác có thể gọi getFoo và mỗi người trong số họ nhận được kết quả tương tự. Sau đó, mỗi người trong số họ thêm một và gọi setFoo, và kết quả cuối cùng là foo được tăng lên chỉ một lần, thay vì hai lần. Như bài viết của bạn chỉ ra, đây là một điều kiện chủng tộc đua.

Để làm cho chủ đề an toàn, cả đọc và viết phải cùng nhau trong cùng một khối được đồng bộ hóa, không có phương thức nhận và đặt riêng biệt.

public synchronized void addFoo(int addend) 
{ 
    foo += addend; 
} 
+0

'Sau đó, mỗi người trong số họ thêm một và gọi setFoo và kết quả cuối cùng là foo chỉ tăng một lần, thay vì hai lần' Tại sao? –

+0

Vì mỗi Chủ đề cập nhật 'foo' thành cùng một giá trị. Ví dụ, mỗi người trong số họ nhận được giá trị 2, mỗi người trong số họ thêm 1 để có được 3, sau đó mỗi người trong số họ đặt giá trị thành 3. – rgettman

+0

Nếu tôi hiểu đúng, thì không nên tuyên bố của bạn là "Sau đó, mỗi người trong số họ thêm một và gọi setFoo, và kết quả cuối cùng là foo được tăng lên ** hai lần **? ' –

1

Bẫy chính trong mã của bạn là có vẻ như getFoo sẽ được gọi là "bên trong" setFoo. Loại

setFoo(){ 
    //getFoo(); 
    //... 
} 

đó là không chính xác bởi vì trong thực tế getFoo được gọi trước khi gọi setFoo.Dưới đây là ví dụ cho thấy điều đó:

public static int foo(int i) { 
    System.out.print("FOO!"); 
    return i; 
} 

public static int bar(int i) { 
    System.out.print("BAR!"); 
    return i; 
} 

public static void main(String[] args) throws Exception { 
    System.out.println(foo(bar(1))); 
} 

Output:

BAR!FOO!1 

Như bạn thấy bar đã được gọi trước khi foo. Vì vậy, trong trường hợp của bạn có thể là hai (hoặc nhiều) chủ đề sẽ gọi getFoo mà sẽ trả về giá trị hiện tại trước khi họ sẽ gọi setFoo. Trong trường hợp này cả họ sẽ có cùng một giá trị, cho phép nói 0 và khi họ sẽ gọi setFoo họ sẽ cả hai thiết lập nó để 1.

0

thực hiện điều này mã giúp đỡ?

class C { 
    private int foo; 
    public int getFoo() { return foo; } 
    public void setFoo(int f) { foo = f; } 
} 

C myC = new C(); 
synchronized(myC) { 
    int foo = myC.getFoo(); 
    myC.setFoo(foo + 1); 
} 
println(myC.foo); 
+0

Vì vậy, vấn đề là' myC.setFoo (1) 'có thể được gọi trước' println (myC.getFoo()) '? –

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