Có an toàn để gọi một phương thức được đồng bộ hóa từ một phương thức được đồng bộ hóa khác không?


81

Nếu một phương thức đồng bộ gọi một phương thức đồng bộ khác, thì nó có an toàn không?

void synchronized method1() {
     method2()
}

void synchronized method2() {
}

Bài viết này có giúp giải đáp được không, hay bạn đang bối rối ở đâu? kalyanchakravarthy.net/?p=413
James Black

Có - bạn không thực sự cần đánh dấu method2 là đã đồng bộ hóa giả sử nó chỉ được gọi trong ngữ cảnh đã cho ở trên.
debracey

4
Ngoài ra, nó có phải là threadsafe hay không sẽ phụ thuộc vào những gì xảy ra trong hai phương pháp. Ví dụ: nếu họ gọi danh sách không thuộc luồng an toàn, thì chúng có thể không thuộc luồng an toàn, nếu một số luồng khác có thể sửa đổi tập hợp đó.
James Black

Như một câu trả lời cho những gì tôi đoán là câu hỏi thực tế: vâng, từ khóa được đồng bộ hóa sử dụng khóa đệ quy; bạn có thể gọi một phương thức đồng bộ một cách an toàn từ một phương thức đồng bộ khác.
Brett Kail

Đã được một thời gian, nhưng đây vẫn là lần truy cập đầu tiên trên google, vì vậy: Có, các khối / phương thức được đồng bộ hóa trên cùng một đối tượng đang được sử dụng lại. stackoverflow.com/questions/12219376/reentrant-synchronization
Szocske

Câu trả lời:


103

Có, khi bạn đánh dấu các phương thức là synchronized, thì bạn thực sự đang làm điều này:

void method1() {
    synchronized (this) {
        method2()
    }
}

void method2() {
    synchronized (this) {
    }
}

Khi cuộc gọi luồng đi vào method2 từ method1, thì nó sẽ đảm bảo rằng nó giữ khóa thismà nó sẽ có, và sau đó nó có thể đi qua.

Khi luồng truy cập trực tiếp vào method1 hoặc method2, thì nó sẽ chặn cho đến khi nó có thể nhận được khóa ( this), và sau đó nó sẽ vào.

Như James Black đã lưu ý trong phần nhận xét, bạn phải nhận thức được những gì bạn làm bên trong thân phương thức.

private final List<T> data = new ArrayList<T>();

public synchronized void method1() {
    for (T item : data) {
        // ..
    }
}

public void method3() {
    data.clear();
}

Đột nhiên, nó không an toàn luồng vì bạn đang xem xét một ConcurrentModificationExceptiontrong tương lai của mình vì method3nó không được đồng bộ hóa và do đó có thể được gọi bởi Thread A trong khi Thread B đang hoạt động method1.


Tôi đang cố gắng trả lời một câu hỏi gần giống với câu hỏi được hỏi ở đây. Đây là 2 câu trả lời có thể (2 câu còn lại nói rằng nó sẽ không chạy), câu nào đúng? C. Mã sẽ chạy nhưng có khả năng xảy ra tình huống bế tắc D. Mã sẽ chạy tốt vì Java cung cấp tính năng đồng bộ hóa nhập lại cho phép một luồng có được cùng một khóa nhiều lần ----- Tôi đoán đó là D, nhưng có thể tình huống bế tắc tiềm ẩn là phụ thuộc vào thân phương pháp?

@ user3140993 Mã ở đây không có cơ hội cho sự cố. method3hiển thị các hoạt động phân luồng không an toàn, nhưng bạn đang chú ý đến quá trình đồng bộ hóa nhập lại.
kén chọn vào

7

Là một phương thức được đánh dấu bằng cuộc gọi đồng bộ khác một chuỗi phương thức đồng bộ an toàn.

Nói chung là không thể nói trước được. Nó phụ thuộc vào những gì các phương thức làm và những gì các phương thức khác trên cùng một lớp và các lớp khác làm.

Tuy nhiên, chúng ta có thể chắc chắn rằng các lệnh gọi tới method1 và method2 trên cùng một đối tượng được thực hiện bởi các luồng khác nhau sẽ không thực thi đồng thời. Tùy thuộc vào những gì các phương thức làm, điều này thể đủ để nói rằng lớp là an toàn theo luồng đối với các phương thức này.


2

Từ trang Hướng dẫn Java http://download.oracle.com/javase/tutorial/essential/concurrency/syncmeth.html

  1. Không thể để hai lệnh gọi của các phương thức đồng bộ trên cùng một đối tượng xen kẽ nhau. Khi một luồng đang thực thi một phương thức đồng bộ cho một đối tượng, tất cả các luồng khác gọi các phương thức đồng bộ cho cùng một khối đối tượng (tạm ngừng thực thi) cho đến khi luồng đầu tiên được thực hiện với đối tượng.

  2. khi một phương thức được đồng bộ hóa thoát ra, nó sẽ tự động thiết lập mối quan hệ xảy ra trước với bất kỳ lệnh gọi nào tiếp theo của một phương thức đồng bộ hóa cho cùng một đối tượng. Điều này đảm bảo rằng các thay đổi đối với trạng thái của đối tượng được hiển thị cho tất cả các luồng

Vì vậy, Java sẽ đảm bảo rằng nếu 2 luồng đang thực thi cùng một phương thức, các phương thức sẽ không được thực thi đồng thời mà nối tiếp nhau.

Nhưng bạn cần lưu ý về vấn đề Liveness, http://download.oracle.com/javase/tutorial/essential/concurrency/starvelive.html

Và cho dù bạn đang khóa không liên tục, nguyên nhân trong mã bạn đã sử dụng , mã này khóa toàn bộ đối tượng, nếu đối tượng của bạn chỉ cần quyền truy cập đồng bộ vào một biến, bạn chỉ nên khóa biến đó.


@Stephen Lee - bạn không thể khóa một biến. Sau đó, bạn nói rằng synchronized (this.someVar)bạn đang tìm kiếm đối tượng có tham chiếu được lưu giữ someVar. Sự phân biệt là rất quan trọng.
Stephen C
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.