Wenn ich zwei Methoden in derselben Klasse synchronisiert habe, können sie gleichzeitig ausgeführt werden?


164

Wenn ich zwei Methoden in derselben Klasse synchronisiert habe, können sie dann gleichzeitig auf demselben Objekt ausgeführt werden ? beispielsweise:

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

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

Ich weiß, dass ich nicht methodA()zweimal auf demselben Objekt in zwei verschiedenen Threads ausgeführt werden kann. das Gleiche in methodB().

Aber kann ich methodB()auf einem anderen Thread laufen, während er methodA()noch läuft? (gleiches Objekt)

Antworten:


148

Beide Methoden sperren denselben Monitor. Daher können Sie sie nicht gleichzeitig von verschiedenen Threads auf demselben Objekt ausführen (eine der beiden Methoden wird blockiert, bis die andere abgeschlossen ist).


1
Ich hatte eine Ergänzung zu dieser Frage. Angenommen, beide Methoden sind statisch, jetzt wird methodA mit Class aufgerufen, während methodB mit einem Objekt wie A.methodA () in t1 und obj.methodB () in t2 aufgerufen wird. Was wird jetzt passieren, werden sie blockieren ????
Amod

2
@ amod0017: obj.methodB()ist gleichbedeutend mit A.methodB()wann methodB()ist static. Daher ja, sie blockieren (auf dem Monitor der Klasse, nicht des Objekts).
NPE

Ich werde versuchen, darauf zurückzukommen. :)
Amod

@NPE Selbst wenn beide Methoden statisch sind und 2 Threads t1 und t2 für dasselbe Objekt gleichzeitig versuchen, methodA () und methodB () aufzurufen, wird nur 1 (z. B. t1) Thread ausgeführt und der andere Thread muss warten, bis t1 die Sperre aufhebt ?
Sreeprasad

8
Beachten Sie, dass statische Methoden die .classObjektsperre verwenden. Also wenn du hast class A {static synchronized void m() {} }. Und dann ruft ein Thread auf, dass new A().m()er die Sperre erhältnew A() Objekt . Wenn dann ruft ein anderer Thread A.m()es DAS VERFAHREN KEIN PROBLEM TRITT weil das, was es sieht für Sperre aktiviert ist A.classObjekt , während NO THREADS diese Art der Sperre besitzen . Also auch wenn Sie Methode deklariert synchronizedes actualy zugegriffen wird von zwei verschiedenen Threads ZUGLEICH . Also: Verwenden Sie niemals Objektreferenzen, um statische Methoden
aufzurufen

113

Im Beispiel sind Methode A und Methode B Instanzmethoden (im Gegensatz zu statischen Methoden). Puttensynchronized einer Instanzmethode bedeutet, dass der Thread die Sperre (die "intrinsische Sperre") für die Objektinstanz erwerben muss, für die die Methode aufgerufen wird, bevor der Thread mit der Ausführung von Code in dieser Methode beginnen kann.

Wenn zwei verschiedene Instanzmethoden als synchronisiert markiert sind und verschiedene Threads diese Methoden gleichzeitig für dasselbe Objekt aufrufen, kämpfen diese Threads um dieselbe Sperre. Sobald ein Thread die Sperre erhält, werden alle anderen Threads von allen synchronisierten Instanzmethoden für dieses Objekt ausgeschlossen.

Damit die beiden Methoden gleichzeitig ausgeführt werden können, müssten sie unterschiedliche Sperren verwenden:

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

Dabei ermöglicht die synchronisierte Blocksyntax die Angabe eines bestimmten Objekts, für das der ausführende Thread die intrinsische Sperre erhalten muss, um in den Block einzutreten.

Es ist wichtig zu verstehen, dass, obwohl wir einzelne Methoden mit einem "synchronisierten" Schlüsselwort versehen, das Kernkonzept die intrinsische Sperre hinter den Kulissen ist.

So beschreibt das Java-Tutorial die Beziehung:

Die Synchronisierung basiert auf einer internen Entität, die als intrinsische Sperre oder Monitorsperre bezeichnet wird. (In der API-Spezifikation wird diese Entität häufig einfach als "Monitor" bezeichnet.) Intrinsische Sperren spielen bei beiden Aspekten der Synchronisation eine Rolle: Erzwingen des exklusiven Zugriffs auf den Status eines Objekts und Herstellen von Vor-Vor-Beziehungen, die für die Sichtbarkeit wesentlich sind.

Jedem Objekt ist eine intrinsische Sperre zugeordnet. Gemäß der Konvention muss ein Thread, der exklusiven und konsistenten Zugriff auf die Felder eines Objekts benötigt, die intrinsische Sperre des Objekts abrufen, bevor er auf sie zugreift, und dann die intrinsische Sperre aufheben, wenn sie mit ihnen abgeschlossen ist. Ein Thread soll die intrinsische Sperre zwischen dem Zeitpunkt, zu dem er die Sperre erworben und die Sperre aufgehoben hat, besitzen. Solange ein Thread eine intrinsische Sperre besitzt, kann kein anderer Thread dieselbe Sperre erhalten. Der andere Thread wird blockiert, wenn er versucht, die Sperre zu erlangen.

Der Zweck des Sperren besteht darin, gemeinsam genutzte Daten zu schützen. Sie würden separate Sperren verwenden, wie im obigen Beispielcode gezeigt, nur wenn jede Sperre unterschiedliche Datenelemente schützt.


In diesem Beispiel befindet sich die Sperre also für die Objekte lockA \ lockB und nicht für die Klasse A? Ist dies ein Beispiel für das Sperren auf Klassenebene ?
Nimrod

2
@Nimrod: Es wird für LockA- und LockB-Objekte gesperrt und nicht für die Instanz von A. Hier wird nichts für eine Klasse gesperrt. Sperren auf Klassenebene würde bedeuten, die Sperre für ein Klassenobjekt mit etwas wie static synchronizedodersynchronized (A.class)
Nathan Hughes

Hier ist der Link zum Java-Tutorial, in dem genau erklärt wird, was hier beantwortet wird.
Alberto de Paola

18

Java Thread erwirbt eine Sperre auf Objektebene, wenn es in eine instanzsynchronisierte Java-Methode eingeht , und eine Sperre auf Klassenebene, wenn es in eine statische synchronisierte Java-Methode eingeht .

In Ihrem Fall gehören die Methoden (Instanzen) zur selben Klasse. Wenn also ein Thread in eine mit Java synchronisierte Methode oder einen Block eintritt, erhält er eine Sperre (das Objekt, für das die Methode aufgerufen wird). Daher kann eine andere Methode nicht gleichzeitig für dasselbe Objekt aufgerufen werden, bis die erste Methode abgeschlossen und die Sperre (für das Objekt) aufgehoben wurde.


Wenn ich zwei Threads auf zwei verschiedenen Instanzen der Klasse habe, können sie beide Methoden gleichzeitig ausführen, sodass ein Thread eine synchronisierte Methode und der andere die zweite synchronisierte Methode aufruft. Wenn mein Verständnis korrekt ist, kann ich dann private final Object lock = new object();mit synchronisiert verwenden, damit nur ein Thread eine der beiden Methoden ausführen kann? Vielen Dank
Yug Singh

13

In Ihrem Fall haben Sie zwei Methoden auf derselben Klasseninstanz synchronisiert. Daher können diese beiden Methoden nicht gleichzeitig auf verschiedenen Threads derselben Instanz der Klasse A ausgeführt werden. Sie können jedoch auf verschiedenen Instanzen der Klasse A ausgeführt werden.

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

ist das gleiche wie:

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

Was passiert, wenn ich eine Sperre als definiere private final Object lock = new Object();und sie jetzt lockmit synchronisiertem Block in zwei Methoden verwende? Gilt Ihre Aussage dann? IMO, da Object die übergeordnete Klasse aller Objekte ist. Selbst wenn sich die Threads auf einer anderen Instanz der Klasse befinden, kann jeweils nur einer auf den Code innerhalb des synchronisierten Blocks zugreifen. Vielen Dank.
Yug Singh

Wenn Sie in der Klasse "private endgültige Objektsperre" definieren und mit dieser synchronisieren, füllen Sie die Sperre pro Klasseninstanz aus, sodass sie sich genauso verhält wie synchronisiert (dies).
Oleksandr_DJ

Ja, Object ist ein übergeordnetes Element für alle Klassen, aber die Instanz "lock" ist in Ihrem Fall "Instanz pro besitzender Klasse". Daher hat sie für die Synchronisierung den gleichen Effekt wie "this".
Oleksandr_DJ

7

Aus der Orakel-Dokumentation Link

Das Synchronisieren von Methoden hat zwei Auswirkungen:

Erstens ist es nicht möglich, dass zwei Aufrufe synchronisierter Methoden für dasselbe Objekt verschachtelt werden. Wenn ein Thread eine synchronisierte Methode für ein Objekt ausführt, blockieren alle anderen Threads, die synchronisierte Methoden für denselben Objektblock aufrufen (Ausführung aussetzen), bis der erste Thread mit dem Objekt fertig ist.

Zweitens wird beim Beenden einer synchronisierten Methode automatisch eine Vorher-Beziehung zu einem nachfolgenden Aufruf einer synchronisierten Methode für dasselbe Objekt hergestellt. Dies garantiert, dass Änderungen am Status des Objekts für alle Threads sichtbar sind

Dies beantwortet Ihre Frage: Für dasselbe Objekt können Sie keine zweite synchronisierte Methode aufrufen, wenn die erste synchronisierte Methodenausführung ausgeführt wird.

Schauen Sie sich diese Dokumentation an Seite intrinsische Sperren und Sperrverhalten zu verstehen.


6

Stellen Sie sich Ihren Code wie folgt vor:

class A {

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

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

Auf Methodenebene synchronisiert bedeutet also einfach synchronisiert (dies). Wenn ein Thread eine Methode dieser Klasse ausführt, erhält er die Sperre vor dem Start der Ausführung und hält sie, bis die Ausführung der Methode abgeschlossen ist.

Aber kann ich methodB () auf einem anderen Thread ausführen, während methodA () noch ausgeführt wird? (gleiches Objekt)

In der Tat ist es nicht möglich!

Daher können mehrere Threads nicht mehrere synchronisierte Methoden gleichzeitig für dasselbe Objekt ausführen.


Was ist, wenn ich Threads für zwei verschiedene Objekte derselben Klasse erstelle? In diesem Fall Wenn sie eine Methode aus einem Thread und eine andere Methode aus dem zweiten Thread aufrufen, werden sie dann nicht gleichzeitig ausgeführt?
Yug Singh

2
Sie werden, weil sie verschiedene Objekte sind. Das heißt, wenn Sie dies verhindern möchten, können Sie statische Methoden verwenden und die Klasse synchronisieren oder ein Klassenvariablenobjekt als Sperre verwenden oder die Klasse Singleton erstellen. @ Yug Singh
Khosro Makari

4

Aus Gründen der Klarheit ist es möglich, dass sowohl statisch synchronisierte als auch nicht statisch synchronisierte Methoden gleichzeitig oder gleichzeitig ausgeführt werden können, da eine Sperre auf Objektebene und eine Sperre auf Klassenebene vorhanden ist.


3

Die Schlüsselidee bei der Synchronisierung, die sich nicht leicht einfügt, ist, dass sie nur dann wirksam wird, wenn Methoden für dasselbe Objekt aufgerufen werden - dies wurde bereits in den Antworten und Kommentaren hervorgehoben.

Das folgende Beispielprogramm soll das gleiche klar bestimmen -

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

Beachten Sie den Unterschied in der Ausgabe, wie der gleichzeitige Zugriff wie erwartet zulässig ist wenn Methoden für verschiedene Objektinstanzen aufgerufen werden.

Ausgabe mit noEffectOfSynchronizedAsMethodsCalledOnDifferentObjects () kommentiert - die Ausgabe erfolgt in der Reihenfolge methodA in> methodA Out .. methodB in> methodB Out Ausgabe mit * noEffectOfSynchronizedAsMethodsCalledOnDifferentObjects () * kommentiert

und Ausgabe mit synchronizedEffectiveAsMethodsCalledOnSameObject () kommentiert - die Ausgabe zeigt den gleichzeitigen Zugriff von Methode1 durch Thread1 und Thread0 im hervorgehobenen Abschnitt -

Ausgabe mit * synchronizedEffectiveAsMethodsCalledOnSameObject () * kommentiert

Wenn Sie die Anzahl der Threads erhöhen, wird dies noch deutlicher.


2

Nein, es ist nicht möglich, wenn es möglich wäre, könnten beide Methoden dieselbe Variable gleichzeitig aktualisieren, was die Daten leicht beschädigen könnte.


2

Ja, sie können beide Threads gleichzeitig ausführen. Wenn Sie 2 Objekte der Klasse erstellen, da jedes Objekt nur eine Sperre enthält und jede synchronisierte Methode eine Sperre erfordert. Wenn Sie also gleichzeitig ausgeführt werden möchten, erstellen Sie zwei Objekte und versuchen Sie dann, diese Objektreferenz zu verwenden.


1

Sie synchronisieren es für ein Objekt, nicht für eine Klasse. Sie können also nicht gleichzeitig auf demselben Objekt ausgeführt werden


0

Zwei verschiedene Threads, die eine gemeinsame synchronisierte Methode für das einzelne Objekt ausführen. Da das Objekt dasselbe ist und ein Thread es mit einer synchronisierten Methode verwendet, muss die Sperre geändert werden. Wenn die Sperre aktiviert ist, wird dieser Thread in den Wartezustand versetzt. Wenn die Sperre deaktiviert ist, kann sie auf das Objekt zugreifen. Wenn sie darauf zugreift, wird die Sperre aktiviert und die Sperre wird erst freigegeben, wenn die Ausführung abgeschlossen ist. Wenn der andere Thread eintrifft, wird die Sperre geändert, da sie aktiviert ist. Sie wartet, bis der erste Thread seine Ausführung abgeschlossen hat, und gibt die Sperre für das Objekt frei. Sobald die Sperre aufgehoben wird, erhält der zweite Thread Zugriff auf das Objekt und Dadurch wird die Sperre aktiviert, bis sie ausgeführt wird. Damit die Ausführung nicht gleichzeitig erfolgt, werden beide Threads einzeln ausgeführt.


1
Bitte interpunktieren und kapitalisieren Sie dieses Durcheinander richtig. Es gibt kein Wort wie "variieren".
Marquis von Lorne
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.