Jeśli zsynchronizuję dwie metody w tej samej klasie, czy mogą one działać jednocześnie?

164

Jeśli zsynchronizowałem dwie metody w tej samej klasie, czy mogą one działać jednocześnie na tym samym obiekcie ? na przykład:

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

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

Wiem, że nie mogę uruchomić methodA()dwa razy tego samego obiektu w dwóch różnych wątkach. to samo w methodB().

Ale czy mogę uruchomić methodB()inny wątek, gdy methodA()jest on nadal uruchomiony? (ten sam obiekt)

Shelef
źródło

Odpowiedzi:

148

Obie metody blokują ten sam monitor. Dlatego nie można ich jednocześnie wykonywać na tym samym obiekcie z różnych wątków (jedna z dwóch metod będzie blokować, dopóki druga nie zostanie zakończona).

NPE
źródło
1
Miałem dodatek do tego pytania. Załóżmy, że obie metody są statyczne, teraz metoda methodA jest wywoływana przy użyciu Class, podczas gdy metoda MethodB jest wywoływana przy użyciu obiektu takiego jak A.methodA () w t1 i obj.methodB () w t2. Co się teraz stanie, czy zablokują ????
amod
2
@ amod0017: obj.methodB()jest synonimem A.methodB()kiedy methodB()jest static. Dlatego tak, będą blokować (na monitorze klasy, a nie obiektu).
NPE
spróbuje wrócić do tego. :)
amod
@NPE Więc nawet jeśli obie metody są statyczne i 2 wątki t1 i t2 na tym samym obiekcie próbują wywołać metodę MethodA () i methodB () jednocześnie, to tylko 1 (powiedzmy t1) wątek zostanie wykonany, a drugi wątek musi poczekać, aż t1 zwolni blokadę ?
sreeprasad
8
Pamiętaj, że metody statyczne używają blokady .classobiektu. Więc jeśli masz class A {static synchronized void m() {} }. A potem jeden wątek wywołuje new A().m()blokadę new A()obiektu. Jeśli wtedy inny wątek wywołaA.m() to WPISZ METODĘ BEZ PROBLEMU, ponieważ to, czego szuka, to zablokowanie A.classobiektu, podczas gdy ŻADNE NITKI nie mają tego rodzaju blokady . Więc nawet jeśli zadeklarowałeś metodę, synchronizedto faktycznie JEST ona dostępna przez dwa różne wątki W TYM SAMYM CZASIE . Zatem: nigdy nie używaj odwołań do obiektów do wywoływania metod statycznych
Alex Semeniuk
113

W przykładzie methodA i methodB są metodami instancji (w przeciwieństwie do metod statycznych). Wprowadzenie synchronizedmetody instancji oznacza, że ​​wątek musi uzyskać blokadę („wewnętrzną blokadę”) na instancji obiektu, do której metoda jest wywoływana, zanim wątek będzie mógł rozpocząć wykonywanie dowolnego kodu w tej metodzie.

Jeśli masz dwie różne metody wystąpienia oznaczone jako zsynchronizowane, a różne wątki wywołują te metody jednocześnie na tym samym obiekcie, te wątki będą rywalizować o tę samą blokadę. Gdy jeden wątek zostanie zablokowany, wszystkie inne wątki zostaną odcięte od wszystkich zsynchronizowanych metod instancji tego obiektu.

Aby te dwie metody działały jednocześnie, musiałyby używać różnych blokad, na przykład:

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
        }
    }
}

gdzie zsynchronizowana składnia bloku pozwala określić konkretny obiekt, którego wątek wykonawczy musi uzyskać wewnętrzną blokadę, aby wejść do bloku.

Ważną rzeczą do zrozumienia jest to, że chociaż umieszczamy słowo kluczowe „zsynchronizowane” na poszczególne metody, podstawową koncepcją jest wewnętrzna blokada za kulisami.

Oto jak samouczek Java opisuje tę relację:

Synchronizacja jest zbudowana wokół wewnętrznej jednostki znanej jako wewnętrzna blokada lub blokada monitora. (Specyfikacja API często odnosi się do tej jednostki po prostu jako do „monitora”). Wewnętrzne blokady odgrywają rolę w obu aspektach synchronizacji: wymuszaniu wyłącznego dostępu do stanu obiektu i ustanawianiu relacji zdarzają się przed, które są niezbędne dla widoczności.

Każdy obiekt jest powiązany z wewnętrzną blokadą. Zgodnie z konwencją, wątek, który potrzebuje wyłącznego i spójnego dostępu do pól obiektu, musi uzyskać wewnętrzną blokadę obiektu przed uzyskaniem do nich dostępu, a następnie zwolnić wewnętrzną blokadę, gdy zostanie z nimi zakończona. Mówi się, że wątek posiada wewnętrzną blokadę między momentem uzyskania blokady a zwolnieniem blokady. Tak długo, jak wątek posiada wewnętrzną blokadę, żaden inny wątek nie może uzyskać takiej samej blokady. Drugi wątek zostanie zablokowany, gdy spróbuje uzyskać blokadę.

Celem blokowania jest ochrona udostępnianych danych. Możesz użyć oddzielnych blokad, jak pokazano w powyższym przykładowym kodzie, tylko wtedy, gdy każdy zamek chronił różnych członków danych.

Nathan Hughes
źródło
więc w tym przykładzie blokada jest na obiektach lockA \ lockB, a nie na obiektach klasy A? Czy to przykład blokowania na poziomie klasy ?
Nimrod,
2
@Nimrod: blokuje obiekty lockA i lockB, a nie instancję A. nic tutaj nie blokuje klasy. blokowanie na poziomie klasy oznaczałoby zablokowanie obiektu klasy za pomocą czegoś takiego jak static synchronizedlubsynchronized (A.class)
Nathan Hughes
Oto link do samouczka Java, wyjaśniającego dokładnie, na co tutaj udzielono odpowiedzi.
Alberto de Paola
18

Java Thread uzyskuje blokadę na poziomie obiektu, gdy wchodzi do zsynchronizowanej metody Java z instancją , a blokuje na poziomie klasy, gdy przechodzi do statycznej zsynchronizowanej metody Java.

W twoim przypadku metody (instancja) są tej samej klasy. Więc kiedy wątek wchodzi do metody lub bloku zsynchronizowanego z Javą, uzyskuje blokadę (obiekt, na którym metoda jest wywoływana). Dlatego nie można wywołać innej metody w tym samym czasie na tym samym obiekcie, dopóki pierwsza metoda nie zostanie zakończona i nie zostanie zwolniona blokada (na obiekcie).

Srikanth
źródło
jeśli mam dwa wątki w dwóch różnych wystąpieniach klasy, będą one mogły wykonywać obie metody jednocześnie, tak że jeden wątek wywołuje jedną metodę synchronizowaną, a drugi wywołuje drugą metodę synchronizowaną. Jeśli moje rozumienie jest poprawne, czy mogę używać private final Object lock = new object();z synchronizowanym, aby umożliwić tylko jednemu wątkowi wykonanie jednej z metod? Dzięki
Yug Singh
13

W twoim przypadku zsynchronizowałeś dwie metody na tej samej instancji klasy. Tak więc te dwie metody nie mogą działać jednocześnie w innym wątku tej samej instancji klasy A. Ale mogą działać na różnych instancjach klasy A.

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

jest taki sam jak:

class A {
    public void methodA() {
        synchronized(this){
            // code of method A
        }
    }
}
Oleksandr_DJ
źródło
co, jeśli zdefiniuję blokadę jako private final Object lock = new Object();i teraz używam lockz synchronizowanym blokiem w dwóch metodach, to czy twoje oświadczenie będzie prawdziwe? IMO, ponieważ Object jest klasą nadrzędną wszystkich obiektów, więc nawet jeśli wątki znajdują się w różnych instancjach tej klasy, tylko jeden może uzyskać dostęp do kodu w zsynchronizowanym bloku na raz. Dzięki.
Yug Singh
Jeśli zdefiniujesz "private final Object lock" w klasie i zsynchronizujesz się z nią, wypełnisz blokadę dla każdej instancji klasy, więc zachowa się ona tak samo jak zsynchronizowana (this).
Oleksandr_DJ
Tak, Object jest rodzicem dla wszystkich klas, ale instancja „lock” w twoim przypadku to „instancja na klasę będącą właścicielem”, więc ma taki sam efekt jak „this” w przypadku synchronizacji.
Oleksandr_DJ
7

Z linku do dokumentacji Oracle

Synchronizacja metod ma dwa skutki:

Po pierwsze, nie jest możliwe, aby dwa wywołania zsynchronizowanych metod na tym samym obiekcie miały przeplot. Gdy jeden wątek wykonuje zsynchronizowaną metodę dla obiektu, wszystkie inne wątki, które wywołują zsynchronizowane metody dla tego samego bloku obiektu (wstrzymują wykonywanie), dopóki pierwszy wątek nie zostanie zakończony z obiektem.

Po drugie, kiedy zsynchronizowana metoda kończy działanie, automatycznie ustanawia relację zdarzenie przed każdym kolejnym wywołaniem metody synchronicznej dla tego samego obiektu. Gwarantuje to, że zmiany stanu obiektu są widoczne dla wszystkich wątków

To odpowie na twoje pytanie: na tym samym obiekcie nie możesz wywołać drugiej zsynchronizowanej metody, gdy pierwsza zsynchronizowana metoda jest wykonywana.

Spójrz na tę stronę dokumentacji, aby zrozumieć wewnętrzne blokady i zachowanie blokad.

Aditya W
źródło
6

Pomyśl o swoim kodzie jak o poniższym:

class A {

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

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

Zatem synchronizacja na poziomie metody oznacza po prostu synchronizację (to). jeśli jakikolwiek wątek uruchamia metodę tej klasy, uzyskałby blokadę przed rozpoczęciem wykonywania i wstrzymałby ją do zakończenia wykonywania metody.

Ale czy mogę uruchomić metodę MethodB () w innym wątku, podczas gdy methodA () nadal działa? (ten sam obiekt)

Rzeczywiście, nie jest to możliwe!

W związku z tym wiele wątków nie będzie w stanie jednocześnie uruchomić dowolnej liczby zsynchronizowanych metod na tym samym obiekcie.

Khosro Makari
źródło
co się stanie, jeśli utworzę wątki na dwóch różnych obiektach tej samej klasy? W takim przypadku, jeśli wywołam jedną metodę z jednego wątku i inną metodę z drugiego wątku, czy nie będą one wykonywane jednocześnie?
Yug Singh
2
Będą, ponieważ są różnymi przedmiotami. Oznacza to, że jeśli chcesz temu zapobiec, możesz użyć metod statycznych i zsynchronizować klasę lub użyć obiektu zmiennej klasy jako blokady lub uczynić klasę Singleton. @Yug Singh
Khosro Makari
4

Dla jasności jest możliwe, że zarówno statyczna, jak i niestatyczna metoda synchronizowana mogą działać jednocześnie lub współbieżnie, ponieważ jedna ma blokadę na poziomie obiektu i inną blokadę na poziomie klasy.

pacmanfordinner
źródło
3

Klucz pomysł z synchronizacją, który nie tonąć w łatwo jest to, że będzie to miało wpływ tylko wtedy, gdy metody nazywane są na tym samym obiekcie przykład - to już zostało podkreślone w odpowiedziach i komentarze -

Poniższy przykładowy program ma wyraźnie wskazać to samo -

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();
    }
}
}

Zwróć uwagę na różnicę w wynikach, w jaki sposób jednoczesny dostęp jest dozwolony zgodnie z oczekiwaniami, jeśli metody są wywoływane w różnych wystąpieniach obiektów.

Ouput z noEffectOfSynchronizedAsMethodsCalledOnDifferentObjects () skomentował -the wyjście jest w porządku sposób A w> sposób A schodzi .. MethodB w> MethodB Out Ouput z komentarzem * noEffectOfSynchronizedAsMethodsCalledOnDifferentObjects () *

i Ouput z synchronizedEffectiveAsMethodsCalledOnSameObject () komentuje - wskazywało na jednoczesny dostęp metodaA przez thread1 i Thread0 w zaznaczonej sekcji, -

Ouput z * synchronizedEffectiveAsMethodsCalledOnSameObject () * skomentował

Zwiększenie liczby wątków sprawi, że będzie on jeszcze bardziej zauważalny.

somshivam
źródło
2

Nie, nie jest to możliwe, gdyby było to możliwe, obie metody mogłyby aktualizować tę samą zmienną jednocześnie, co mogłoby łatwo uszkodzić dane.

fastcodejava
źródło
2

Tak, mogą działać jednocześnie oba wątki. Jeśli utworzysz 2 obiekty klasy, ponieważ każdy obiekt zawiera tylko jedną blokadę, a każda zsynchronizowana metoda wymaga blokady. Więc jeśli chcesz działać jednocześnie, utwórz dwa obiekty, a następnie spróbuj uruchomić, używając tych odniesień do obiektów.

coolts
źródło
1

Synchronizujesz go na obiekcie, a nie na klasie. Nie mogą więc działać jednocześnie na tym samym obiekcie

xyz
źródło
0

Dwa różne wątki wykonujące wspólną metodę synchronizowaną na jednym obiekcie, ponieważ obiekt jest taki sam, gdy jeden wątek używa go metodą synchroniczną, będzie musiał zmienić blokadę, jeśli blokada jest włączona, ten wątek przejdzie w stan oczekiwania, jeśli blokada jest wyłączona, wówczas może uzyskać dostęp do obiektu, podczas gdy uzyska dostęp, włączy blokadę i zwolni blokadę dopiero po zakończeniu wykonywania. kiedy nadejdą kolejne wątki, zmieni blokadę, ponieważ jest włączona, będzie czekała, aż pierwszy wątek zakończy swoje wykonanie i zwolni blokadę nałożoną na obiekt, po zwolnieniu blokady drugi wątek uzyska dostęp do obiektu i włączy blokadę do czasu jej wykonania. więc wykonanie nie będzie współbieżne, oba wątki będą wykonywane jeden po drugim,

Ankit yadav
źródło
1
Prosimy o poprawną interpunkcję i pisanie na wielką literę tego bałaganu. Nie ma słowa „zróżnicować”.
Markiz Lorne,