2010-06-15 8 views
131

Wenn ich zwei synchronisierte Methoden in derselben Klasse habe, aber alle auf unterschiedliche Variablen zugreifen, können 2 Threads gleichzeitig auf diese beiden Methoden zugreifen? Tritt die Sperre für das Objekt auf oder wird sie so spezifisch wie die Variablen in der synchronisierten Methode?Java synchronisierte Methodensperre für Objekt oder Methode?

Beispiel:

class X { 

    private int a; 
    private int b; 

    public synchronized void addA(){ 
     a++; 
    } 

    public synchronized void addB(){ 
     b++; 
    } 

} 

Can 2 Fäden die gleiche Instanz von Klasse X Zugriffs x.addA() und x.addB() zugleich?

Antwort

137

Wenn Sie deklarieren die Methode als synchonized (wie Sie durch Eingabe von public synchronized void addA() tun) synchronisieren Sie auf das ganzen Objekt, so dass zwei Fäden eine andere Variable aus demselben Objekt zugreifen würden sie sowieso blockieren.

Wenn Sie nur eine Variable gleichzeitig synchronisieren möchten, damit sich zwei Threads beim Zugriff auf andere Variablen nicht gegenseitig blockieren, müssen Sie sie in synchronized() Blöcken separat synchronisieren. Wenn a und b Objektreferenzen waren verwenden Sie:

public void addA() { 
    synchronized(a) { 
     a++; 
    } 
} 
public void addB() { 
    synchronized(b) { 
     b++; 
    } 
} 

Aber da sie Primitiven sind Sie dies nicht tun können.

Ich würde vorschlagen, Sie Atomicinteger zu verwenden, anstatt:

import java.util.concurrent.atomic.AtomicInteger; 
class X { 
    AtomicInteger a; 
    AtomicInteger b; 
    public void addA(){ 
     a.incrementAndGet(); 
    } 
    public void addB(){ 
     b.incrementAndGet(); 
    } 
} 
+121

* Wenn Sie bei der Methode synchronisieren, sperren Sie das ganze Objekt, so dass zwei Threads, die auf eine andere Variable von demselben Objekt zugreifen, sich gegenseitig blockieren würden. * Das ist ein bisschen irreführend. Synchronisieren mit der Methode ist funktional äquivalent zu einem "synchronisierten" Block um den Körper der Methode herum. Das Objekt "this" wird nicht gesperrt, sondern das Objekt "this" wird als Mutex verwendet, und der Körper wird daran gehindert, gleichzeitig mit anderen Codeabschnitten zu arbeiten, die auch auf "this" synchronisiert sind. Es wirkt sich nicht auf andere Felder/Methoden von "this" aus, die nicht synchronisiert sind. –

+9

Ja, es ist wirklich irreführend. Für ein echtes Beispiel - Sehen Sie sich das an - http://StackOverflow.com/questions/14447095/does-java-monitor-include-instance-variables - Zusammenfassung: Das Sperren erfolgt nur auf synchronisierter Methodenebene, und die Instanzvariablen des Objekts können von anderen zugegriffen werden Thread – mac

+3

Das erste Beispiel ist grundlegend gebrochen. Wenn "a" und "b" Objekte waren, z. 'Integer's, Sie haben auf Instanzen synchronisiert, die Sie * mit anderen Objekten * ersetzen, wenn Sie den Operator' ++ 'anwenden. – Holger

12

Der Zugriff auf das Schloss erfolgt auf dem Objekt, nicht auf der Methode. Auf welche Variablen innerhalb der Methode zugegriffen wird, ist irrelevant.

Hinzufügen "synchronisiert" zu der Methode bedeutet, dass der Thread, der den Code ausführt, die Sperre für das Objekt erwerben muss, bevor Sie fortfahren. Das Hinzufügen von "statisch synchronisiert" bedeutet, dass der Thread, der den Code ausführt, die Sperre für das Klassenobjekt erhalten muss, bevor er fortfährt. Alternativ können Sie Code in einen Block wie folgt einbinden:

public void addA() { 
    synchronized(this) { 
     a++; 
    } 
} 

, so dass Sie das Objekt angeben können, dessen Sperre erworben werden muss.

Wenn Sie auf das Objekt enthält, zu vermeiden Sperren können Sie wählen zwischen:

44

Synchronisiert auf die Methodendeklaration ist syntaktischer Zucker dafür:

public void addA() { 
    synchronized (this) { 
      a++; 
    } 
    } 

Auf einer statische Methode ist es syntaktischer Zucker für diesen:

ClassA { 
    public static void addA() { 
      synchronized(ClassA.class) { 
       a++; 
      } 
} 

Ich denke, wenn die Java-Designer dann wussten, was jetzt über die Synchronisation verstanden wird, würde sie den syntaktischen Zucker, da sie häufiger als nicht führt nicht hinzugefügt zu schlechten Implementierungen von Nebenläufigkeit.

+3

Nicht wahr arbeiten. synchronisierte Methode erzeugt einen anderen Bytecode als synchronisiert (Objekt). Während Funktionalität äquivalent ist, ist es mehr als nur syntaktischer Zucker. –

+9

Ich glaube nicht, dass "syntaktischer Zucker" streng als Byte-Code-Äquivalent definiert ist. Der Punkt ist, dass es funktional gleichwertig ist. – Yishai

+1

Hätten die Java-Entwickler gewusst, was an Monitoren * schon * bekannt ist, hätten/hätten sie das anders machen sollen, anstatt die Innereien von Unix nachzuahmen. [Per Brinch Hansen sagte: "Klar habe ich vergebens gearbeitet", als er die Java-Parallelität-Primitive sah (https://forums.oracle.com/thread/1142765?start=0&tstart=342). – EJP

2

Sie so etwas wie die folgenden tun. In diesem Fall verwenden Sie die Sperre auf a und b, um die Sperre anstelle von "this" zu synchronisieren.Wir können nicht int verwenden, da Grundwerte keine Sperren haben, also verwenden wir Integer.

class x{ 
    private Integer a; 
    private Integer b; 
    public void addA(){ 
     synchronized(a) { 
     a++; 
     } 
    } 
    public synchronized void addB(){ 
     synchronized(b) { 
     b++; 
     } 
    } 
} 
0

Dies könnte nicht als Box arbeiten und Autoboxing von Integer in int und umgekehrt ist abhängig von JVM und besteht eine hohe Wahrscheinlichkeit, dass zwei verschiedene Zahlen könnten zur gleichen Adresse gehasht erhalten, wenn sie zwischen -128 und 127 sind.

2

Wenn Sie einige Methoden haben, die nicht synchronisiert sind und auf die Instanzvariablen zugreifen und sie ändern. In Ihrem Beispiel:

private int a; 
private int b; 

eine beliebige Anzahl von Threads können diese nicht synchronisiert Methoden zur gleichen Zeit zugreifen, wenn andere Thread in dem synchronisierten Verfahren desselben Objekts ist und Änderungen an Instanzvariablen machen. Für z: -

public void changeState() { 
     a++; 
     b++; 
    } 

Sie müssen das Szenario vermeiden, die nicht synchronisiert Methoden, um die Instanzvariablen zugreifen und es ansonsten gibt Wechsel ist kein Punkt der synchronisierten Methoden.

Im folgenden Szenario: -

class X { 

     private int a; 
     private int b; 

     public synchronized void addA(){ 
      a++; 
     } 

     public synchronized void addB(){ 
      b++; 
     } 
    public void changeState() { 
      a++; 
      b++; 
     } 
    } 

Nur einer der Fäden kann entweder in Adda oder Addb Methode, aber zur gleichen Zeit eine beliebige Anzahl von Threads change Methode eingeben. Keine zwei Threads können gleichzeitig addA und addB eingeben (wegen der Sperrung auf Objektebene), aber gleichzeitig kann eine beliebige Anzahl von Threads in changeState eintreten.

5

von Oracle Dokumentation link

synchronisiert Herstellungsverfahren hat zwei Effekte:

Erstens ist es nicht möglich, dass zwei Anrufungen von synchronisierten Methoden auf demselben Objekt verschachteln. Wenn ein Thread eine synchronisierte Methode für ein Objekt ausführt, werden alle anderen Threads aufgerufen, die synchronisierte Methoden für denselben Objektblock aufrufen (Ausführung aussetzen), bis der erste Thread mit dem Objekt ausgeführt wird.

Zweitens, wenn eine synchronisierte Methode beendet wird, stellt sie automatisch eine "happen-before" -Beziehung mit einem nachfolgenden Aufruf einer synchronisierten Methode für dasselbe Objekt her. Damit ist gewährleistet, dass Änderungen an dem Zustand des Objekts an alle Threads sichtbar sind

einen Blick auf diese Dokumentation haben page intrinsische Sperren und Sperrverhalten zu verstehen.

Dies beantwortet Ihre Frage: Auf demselben Objekt x können Sie nicht x.addA() und x.addB() gleichzeitig aufrufen, wenn eine der synchronisierten Methoden ausgeführt wird.

1

Dieses Beispiel (obwohl nicht schön) kann mehr Einblick in den Schließmechanismus geben.Wenn Inkremente ist synchronisiert und incrementB ist nicht synchronisiert, dann incrementB wird so schnell wie möglich ausgeführt werden, aber wenn incrementB ist auch synchronisiert dann muss es ‚warten‘ für Inkremente zu Ende, vor incrementB kann seine Arbeit machen.

werden beide Methoden auf einzelne Instanz genannt - Objekt, in diesem Beispiel ist es: Job und ‚konkurrierende‘ Fäden sind einThread und Haupt.

Versuchen mit ‚synchronisiert‘ in incrementB und ohne sie, und Sie werden verschiedene Ergebnisse.Wenn incrementB sehen ist ‚ synchronisiert‘ auch dann für Inkremente() warten muss, bis zum Ende . Führen Sie mehrmals jede Variante aus.

class LockTest implements Runnable { 
    int a = 0; 
    int b = 0; 

    public synchronized void incrementA() { 
     for (int i = 0; i < 100; i++) { 
      this.a++; 
      System.out.println("Thread: " + Thread.currentThread().getName() + "; a: " + this.a); 
     } 
    } 

    // Try with 'synchronized' and without it and you will see different results 
    // if incrementB is 'synchronized' as well then it has to wait for incrementA() to finish 

    // public void incrementB() { 
    public synchronized void incrementB() { 
     this.b++; 
     System.out.println("*************** incrementB ********************"); 
     System.out.println("Thread: " + Thread.currentThread().getName() + "; b: " + this.b); 
     System.out.println("*************** incrementB ********************"); 
    } 

    @Override 
    public void run() { 
     incrementA(); 
     System.out.println("************ incrementA completed *************"); 
    } 
} 

class LockTestMain { 
    public static void main(String[] args) throws InterruptedException { 
     LockTest job = new LockTest(); 
     Thread aThread = new Thread(job); 
     aThread.setName("aThread"); 
     aThread.start(); 
     Thread.sleep(1); 
     System.out.println("*************** 'main' calling metod: incrementB **********************"); 
     job.incrementB(); 
    } 
} 
6

From the Java SE essentials on synchronized methods:

Erstens ist es für zwei Invokationen von synchronisierten Methoden auf demselben Objekt verschachteln nicht möglich. Wenn ein Thread eine synchronisierte Methode für ein Objekt ausführt, werden alle anderen Threads aufgerufen, die synchronisierte Methoden für denselben Objektblock aufrufen (Ausführung aussetzen), bis der erste Thread mit dem Objekt ausgeführt wird.

Vom Java SE essentials on synchronized blocks:

Synchronisierte Aussagen sind auch nützlich für die Gleichzeitigkeit mit feinkörnigem Synchronisation zu verbessern. Angenommen, die Klasse MsLunch hat beispielsweise zwei Instanzfelder, c1 und c2, die niemals zusammen verwendet werden. Alle Aktualisierungen dieser Felder müssen synchronisiert werden, , aber es gibt keinen Grund zu verhindern, dass ein Update von c1 mit einem Update von c2 interleaved wird - und dies reduziert die Nebenläufigkeit, indem unnötige Blockierung erzeugt wird. Anstatt synchronisierte Methoden zu verwenden oder die damit verbundene Sperre anderweitig zu verwenden, erstellen wir zwei Objekte, um nur Sperren bereitzustellen.

(Hervorhebung von mir.)

Sie haben zwei Variablen nicht verschachtelt. Sie möchten also gleichzeitig auf verschiedene Threads zugreifen. Sie müssen die Sperre nicht auf der Objektklasse selbst, sondern auf der Object-Klasse wie unten (Beispiel aus dem zweiten Oracle-Link) definieren:

public class MsLunch { 

    private long c1 = 0; 
    private long c2 = 0; 
    private Object lock1 = new Object(); 
    private Object lock2 = new Object(); 

    public void inc1() { 
     synchronized(lock1) { 
      c1++; 
     } 
    } 

    public void inc2() { 
     synchronized(lock2) { 
      c2++; 
     } 
    } 
} 
1

Ja, es blockiert die andere Methode, weil synchronisierte Methode gilt für die GANZE Klasse Objekt wie spitz .... aber trotzdem wird es die andere Thread-Ausführung blockieren NUR während der Durchführung der Summe in welcher Methode addA oder addB es eintritt, denn wenn es fertig ist ...der eine Thread wird FREE das Objekt und der andere Thread wird auf die andere Methode zugreifen und so weiter perfekt funktioniert.

Ich meine, die "synchronisierte" ist genau für das Blockieren des anderen Thread von einem anderen Zugriff während einer bestimmten Codeausführung. SO ENDLICH WIRD DIESER CODE GUT ARBEITEN.

Als letzte Anmerkung, wenn es eine 'a' und 'b' Variablen gibt, nicht nur eine eindeutige Variable 'a' oder was auch immer Name, es gibt keine Notwendigkeit, diese Methoden zu synchronisieren, da es völlig sicher ist var (Anderer Speicherort).

class X { 

private int a; 
private int b; 

public void addA(){ 
    a++; 
} 

public void addB(){ 
    b++; 
}} 

Wird auch