Nếu tôi đồng bộ hóa hai phương thức trên cùng một lớp, chúng có thể chạy đồng thời không?


164

Nếu tôi đồng bộ hóa hai phương thức trên cùng một lớp, chúng có thể chạy đồng thời trên cùng một đối tượng không? ví dụ:

class A {
    public synchronized void methodA() {
        //method A
    }

    public synchronized void methodB() {
        // method B
    }
}

Tôi biết rằng tôi không thể chạy methodA()hai lần trên cùng một đối tượng trong hai luồng khác nhau. điều tương tự trong methodB().

Nhưng tôi có thể chạy methodB()trên các chủ đề khác nhau trong khi methodA()vẫn đang chạy? (cùng đối tượng)

Câu trả lời:


148

Cả hai phương pháp khóa cùng một màn hình. Do đó, bạn không thể đồng thời thực thi chúng trên cùng một đối tượng từ các luồng khác nhau (một trong hai phương thức sẽ chặn cho đến khi kết thúc phương thức kia).


1
Tôi đã có thêm một câu hỏi này. Giả sử cả hai phương thức đều tĩnh, phương thứcA được gọi bằng Class trong khi phương thứcB được gọi bằng cách sử dụng đối tượng như A.methodA () trong t1 và obj.methodB () trong t2. Điều gì sẽ xảy ra bây giờ, họ sẽ chặn ????
Amod

2
@ amod0017: obj.methodB()đồng nghĩa với A.methodB()khi methodB()static. Do đó, họ sẽ chặn (trên lớp, không phải của đối tượng, màn hình).
NPE

sẽ cố gắng và lấy lại cho nó. :)
amod 21/03/13

@NPE Vì vậy, ngay cả khi cả hai phương thức là tĩnh và 2 luồng t1 và t2 trên cùng một đối tượng cố gắng gọi đồng thời phương thứcA () và phương thứcB () thì chỉ có luồng 1 (giả sử t1) sẽ thực thi và luồng khác phải đợi cho đến khi t1 giải phóng khóa ?
sreeprasad

8
Hãy nhớ rằng các phương thức tĩnh sử dụng khóa trên .classđối tượng. Vì vậy, nếu bạn có class A {static synchronized void m() {} }. Và sau đó một luồng gọi new A().m()nó là khóa trên new A()đối tượng. Nếu sau đó, một luồng khác gọi A.m()nó là NHẬP PHƯƠNG PHÁP KHÔNG CÓ VẤN ĐỀ bởi vì những gì nó tìm kiếm là khóa trên A.classđối tượng trong khi KHÔNG CÓ THREADS sở hữu loại khóa này . Vì vậy, mặc dù bạn đã khai báo phương thức synchronizednhưng thực tế IS được truy cập bởi hai luồng khác nhau TẠI THỜI GIAN . Do đó: không bao giờ sử dụng các tham chiếu đối tượng để gọi các phương thức tĩnh
Alex Semeniuk

113

Trong ví dụ phương thứcA và phương thứcB là các phương thức cá thể (trái ngược với các phương thức tĩnh). Đưa synchronizedvào một phương thức cá thể có nghĩa là luồng phải thu được khóa ("khóa nội tại") trên đối tượng mà phương thức được gọi trước khi luồng có thể bắt đầu thực thi bất kỳ mã nào trong phương thức đó.

Nếu bạn có hai phương thức cá thể khác nhau được đánh dấu đồng bộ hóa và các luồng khác nhau đang gọi các phương thức đó đồng thời trên cùng một đối tượng, các luồng đó sẽ tranh nhau cho cùng một khóa. Khi một luồng được khóa, tất cả các luồng khác sẽ bị tắt khỏi tất cả các phương thức cá thể được đồng bộ hóa trên đối tượng đó.

Để hai phương thức chạy đồng thời, chúng sẽ phải sử dụng các khóa khác nhau, như thế này:

class A {
    private final Object lockA = new Object();
    private final Object lockB = new Object();

    public void methodA() {
        synchronized(lockA) {
            //method A
        }
    }

    public void methodB() {
        synchronized(lockB) {
            //method B
        }
    }
}

trong đó cú pháp khối được đồng bộ hóa cho phép chỉ định một đối tượng cụ thể mà luồng thực thi cần có khóa nội tại để vào khối.

Điều quan trọng cần hiểu là mặc dù chúng tôi đang đặt một từ khóa "đồng bộ hóa" cho các phương thức riêng lẻ, khái niệm cốt lõi là khóa nội tại đằng sau hậu trường.

Đây là cách hướng dẫn Java mô tả mối quan hệ:

Đồng bộ hóa được xây dựng xung quanh một thực thể nội bộ được gọi là khóa nội tại hoặc khóa màn hình. .

Mỗi đối tượng có một khóa nội tại liên quan đến nó. Theo quy ước, một luồng cần truy cập độc quyền và nhất quán vào các trường của đối tượng phải có được khóa nội tại của đối tượng trước khi truy cập vào chúng, sau đó giải phóng khóa nội tại khi thực hiện với chúng. Một chủ đề được cho là sở hữu khóa nội tại giữa thời gian nó có được khóa và phát hành khóa. Miễn là một luồng sở hữu một khóa nội tại, không có luồng nào khác có thể có được cùng một khóa. Các luồng khác sẽ chặn khi nó cố gắng để có được khóa.

Mục đích của khóa là để bảo vệ dữ liệu chia sẻ. Bạn sẽ sử dụng các khóa riêng biệt như được hiển thị trong mã ví dụ ở trên chỉ khi mỗi khóa bảo vệ các thành viên dữ liệu khác nhau.


Vì vậy, trong ví dụ này, khóa nằm trên các đối tượng lockA \ lockB chứ không phải trên lớp A? Đây có phải là một ví dụ khóa cấp lớp ?
Nimrod

2
@Nimrod: đó là khóa trên lockA và trên các đối tượng lockB chứ không phải trong trường hợp A. không có gì ở đây là khóa trên một lớp. Khóa cấp độ có nghĩa là có được khóa trên một đối tượng lớp, bằng cách sử dụng một cái gì đó như static synchronizedhoặcsynchronized (A.class)
Nathan Hughes

Đây là liên kết đến hướng dẫn java giải thích chính xác những gì được trả lời ở đây.
Alberto de Paola

18

Java Chủ đề mua lại một khóa cấp đối tượng khi nó đi vào một ví dụ đồng bộ phương pháp java và có được một khóa cấp lớp khi nó xâm nhập vào tĩnh đồng bộ phương pháp java.

Trong trường hợp của bạn, các phương thức (thể hiện) là cùng một lớp. Vì vậy, bất cứ khi nào một luồng nhập vào phương thức được đồng bộ hóa java hoặc chặn nó sẽ có được một khóa (đối tượng mà phương thức được gọi). Vì vậy, phương thức khác không thể được gọi cùng lúc trên cùng một đối tượng cho đến khi phương thức đầu tiên được hoàn thành và khóa (trên đối tượng) được giải phóng.


nếu tôi có hai luồng trên hai phiên bản khác nhau của lớp thì chúng sẽ có thể thực thi đồng thời cả hai phương thức sao cho một luồng gọi một phương thức được đồng bộ hóa và một luồng khác gọi phương thức được đồng bộ hóa thứ hai. Nếu sự hiểu biết của tôi là chính xác thì tôi có thể sử dụng private final Object lock = new object();với đồng bộ hóa để chỉ cho phép một luồng có thể thực thi một trong hai phương thức không? Cảm ơn
Yug Singh

13

Trong trường hợp của bạn, bạn đã đồng bộ hai phương thức trên cùng một thể hiện của lớp. Vì vậy, hai phương thức này không thể chạy đồng thời trên các luồng khác nhau của cùng một thể hiện của lớp A. Nhưng chúng có thể trên các thể hiện của lớp A khác nhau.

class A {
    public synchronized void methodA() {
        //method A
    }
}

giống như:

class A {
    public void methodA() {
        synchronized(this){
            // code of method A
        }
    }
}

Điều gì xảy ra nếu tôi định nghĩa một khóa như private final Object lock = new Object();và bây giờ sử dụng lockvới khối được đồng bộ hóa theo hai phương thức thì câu lệnh của bạn có đúng không? IMO vì Object là lớp cha của tất cả các đối tượng nên ngay cả khi các luồng nằm trong các thể hiện khác nhau của lớp, chỉ một người có thể truy cập mã bên trong khối được đồng bộ hóa tại một thời điểm. Cảm ơn.
Yug Singh

Nếu bạn định nghĩa "khóa đối tượng cuối cùng riêng tư" trong lớp và đồng bộ hóa với nó, bạn điền có khóa cho mỗi thể hiện của lớp, do đó, nó sẽ hoạt động giống như được đồng bộ hóa (điều này).
Oleksandr_DJ

Có, Object là cha mẹ cho tất cả các lớp, nhưng trường hợp "khóa" trong trường hợp của bạn là "thể hiện trên mỗi lớp sở hữu", do đó, nó có tác dụng tương tự như "điều này" để đồng bộ hóa.
Oleksandr_DJ

7

Từ liên kết tài liệu oracle

Làm cho các phương thức được đồng bộ hóa có hai tác dụng:

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

Thứ hai, 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 khi có bất kỳ lời gọi tiếp theo nào của một phương thức đượ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 về trạng thái của đối tượng được hiển thị cho tất cả các luồng

Điều này sẽ trả lời câu hỏi của bạn: Trên cùng một đối tượng, Bạn không thể gọi phương thức được đồng bộ hóa thứ hai khi tiến hành thực hiện phương thức được đồng bộ hóa đầu tiên.

Hãy xem trang tài liệu này để hiểu các khóa nội tại và hành vi khóa.


6

Hãy nghĩ về mã của bạn như dưới đây:

class A {

public void methodA() {
    synchronized(this){        
      //method A body
    }
}

public void methodB() {
    synchronized(this){
      // method B body
    }
}

Vì vậy, đồng bộ hóa ở mức phương thức đơn giản có nghĩa là đồng bộ hóa (điều này). nếu bất kỳ luồng nào chạy một phương thức của lớp này, nó sẽ có được khóa trước khi bắt đầu thực thi và giữ nó cho đến khi việc thực hiện phương thức kết thúc.

Nhưng tôi có thể chạy phương thứcB () trên các luồng khác nhau trong khi phương thứcA () vẫn đang chạy không? (cùng đối tượng)

Thật vậy, điều đó là không thể!

Do đó, nhiều luồng sẽ không thể chạy bất kỳ số lượng phương thức được đồng bộ hóa nào trên cùng một đối tượng.


Điều gì xảy ra nếu tôi tạo Chủ đề trên hai đối tượng khác nhau của cùng một lớp? Trong trường hợp này Nếu tôi gọi một phương thức từ một luồng và phương thức khác từ luồng thứ hai thì chúng có được thực thi đồng thời không?
Yug Singh

2
Họ sẽ bởi vì họ là những đối tượng khác nhau. Đó là, nếu bạn muốn ngăn chặn nó, bạn có thể sử dụng các phương thức tĩnh và đồng bộ hóa lớp hoặc sử dụng một đối tượng biến lớp làm khóa hoặc tạo lớp Singleton. @Yug Singh
Khosro Makari

4

Nói rõ hơn, có thể cả phương thức đồng bộ hóa tĩnh và không đồng bộ tĩnh đều có thể chạy đồng thời hoặc đồng thời vì một người đang có khóa cấp đối tượng và khóa cấp độ lớp khác.


3

Các chính ý tưởng với đồng bộ hóa mà không chìm trong một cách dễ dàng là nó sẽ có hiệu lực chỉ khi phương pháp này được gọi là trên cùng một đối tượng dụ - nó đã được nhấn mạnh trong các câu trả lời và nhận xét -

Dưới đây chương trình mẫu là để xác định rõ ràng giống nhau -

public class Test {

public synchronized void methodA(String currentObjectName) throws InterruptedException {
    System.out.println(Thread.currentThread().getName() + "->" +currentObjectName + "->methodA in");
    Thread.sleep(1000);
    System.out.println(Thread.currentThread().getName() + "->" +currentObjectName + "->methodA out");
}

public synchronized void methodB(String currentObjectName)  throws InterruptedException {
    System.out.println(Thread.currentThread().getName() + "->" +currentObjectName + "->methodB in");
    Thread.sleep(1000);
    System.out.println(Thread.currentThread().getName() + "->" +currentObjectName + "->methodB out");
}

public static void main(String[] args){
    Test object1 = new Test();
    Test object2 = new Test();
    //passing object instances to the runnable to make calls later
    TestRunner runner = new TestRunner(object1,object2);
    // you need to start atleast two threads to properly see the behaviour
    Thread thread1 = new Thread(runner);
    thread1.start();
    Thread thread2 = new Thread(runner);
    thread2.start();
}
}

class TestRunner implements Runnable {
Test object1;
Test object2;

public TestRunner(Test h1,Test h2) {
    this.object1 = h1;
    this.object2 = h2;
}

@Override
public void run() {
    synchronizedEffectiveAsMethodsCalledOnSameObject(object1);
    //noEffectOfSynchronizedAsMethodsCalledOnDifferentObjects(object1,object2);
}

// this method calls the method A and B with same object instance object1 hence simultaneous NOT possible
private void synchronizedEffectiveAsMethodsCalledOnSameObject(Test object1) {
    try {
        object1.methodA("object1");
        object1.methodB("object1");
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

// this method calls the method A and B with different object instances object1 and object2 hence simultaneous IS possible
private void noEffectOfSynchronizedAsMethodsCalledOnDifferentObjects(Test object1,Test object2) {
    try {
        object1.methodA("object1");
        object2.methodB("object2");
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}
}

Lưu ý sự khác biệt về đầu ra về cách cho phép truy cập đồng thời như mong đợi nếu các phương thức được gọi trên các thể hiện đối tượng khác nhau.

Ouput với noEffectOfSynyncizedAsMethodsCalledOnDifferentObjects () đã nhận xét - đầu ra theo thứ tự phương thứcA in> methodA Out .. methodB in> methodB Out Ouput với * noEffectOfSynyncizedAsMethodsCalledOnDifferentObjects () * đã nhận xét

và Ouput với synchronizedEffectiveAsMethodsCalledOnSameObject () nhận xét - cho thấy sản lượng truy cập đồng thời methodA bởi thread1 và Thread0 trong phần nhấn mạnh -

Ouput với * syncEffectiveAsMethodsCalledOnSameObject () * đã nhận xét

Tăng số lượng chủ đề sẽ làm cho nó thậm chí còn đáng chú ý hơn.


2

Không, điều đó là không thể, nếu có thể thì cả hai phương pháp có thể cập nhật cùng một biến đồng thời có thể dễ dàng làm hỏng dữ liệu.


2

Vâng, họ có thể chạy đồng thời cả hai chủ đề. Nếu bạn tạo 2 đối tượng của lớp vì mỗi đối tượng chỉ chứa một khóa và mọi phương thức được đồng bộ hóa đều yêu cầu khóa. Vì vậy, nếu bạn muốn chạy đồng thời, hãy tạo hai đối tượng và sau đó thử chạy bằng cách sử dụng các tham chiếu đối tượng đó.


1

Bạn đang đồng bộ hóa nó trên đối tượng không phải trên lớp. Vì vậy, họ không thể chạy đồng thời trên cùng một đối tượng


0

Hai luồng khác nhau thực thi một phương thức được đồng bộ hóa chung trên một đối tượng, vì đối tượng giống nhau, khi một luồng sử dụng nó với phương thức được đồng bộ hóa, nó sẽ phải thay đổi khóa, nếu khóa được bật, luồng này sẽ chuyển sang trạng thái chờ, nếu khóa bị vô hiệu hóa thì nó có thể truy cập vào đối tượng, trong khi nó truy cập thì nó sẽ kích hoạt khóa và sẽ chỉ giải phóng khóa khi quá trình thực thi hoàn tất. Khi các luồng khác đến, nó sẽ thay đổi khóa, vì khi được bật, nó sẽ đợi cho đến khi luồng đầu tiên hoàn thành việc thực thi và giải phóng khóa được đặt trên đối tượng, khi khóa được giải phóng, luồng thứ hai sẽ có quyền truy cập vào đối tượng và nó sẽ cho phép khóa cho đến khi nó thực thi. vì vậy việc thực thi sẽ không đồng thời, cả hai luồng sẽ thực thi từng cái một,


1
Hãy chấm câu và tận dụng mớ hỗn độn này đúng cách. Không có từ nào như "thay đổi".
Hầu tước Lorne
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.