Se ho sincronizzato due metodi sulla stessa classe, possono essere eseguiti contemporaneamente?


164

Se ho sincronizzato due metodi sulla stessa classe, possono essere eseguiti contemporaneamente sullo stesso oggetto ? per esempio:

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

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

So che non posso correre methodA()due volte sullo stesso oggetto in due thread diversi. stessa cosa dentro methodB().

Ma posso eseguire methodB()thread diversi mentre methodA()è ancora in esecuzione? (stesso oggetto)

Risposte:


148

Entrambi i metodi bloccano lo stesso monitor. Pertanto, non è possibile eseguirli contemporaneamente sullo stesso oggetto da thread diversi (uno dei due metodi si bloccherà fino a quando l'altro non sarà terminato).


1
Ho aggiunto una domanda a questa domanda. Supponiamo che entrambi i metodi siano statici ora il metodo A viene chiamato utilizzando Class mentre methodB viene chiamato utilizzando un oggetto come A.methodA () in t1 e obj.methodB () in t2. Cosa succederà ora, bloccheranno ????
amod

2
@ amod0017: obj.methodB()è sinonimo di A.methodB()quando methodB()è static. Quindi sì, bloccheranno (sul monitor della classe, non dell'oggetto).
NPE

proverò a tornare indietro. :)
amod

@NPE Quindi, anche se entrambi i metodi sono statici e 2 thread t1 e t2 sullo stesso oggetto tentano di chiamare contemporaneamente methodA () e methodB (), verrà eseguito solo 1 thread (diciamo t1) e l'altro thread deve attendere fino a quando t1 rilascia il blocco ?
sreeprasad,

8
Tieni presente che i metodi statici utilizzano il blocco .classsull'oggetto. Quindi se hai class A {static synchronized void m() {} }. E poi un thread chiama new A().m()acquisisce il blocco new A()sull'oggetto. Se poi un altro thread chiama A.m()lo ENTRA NEL METODO NESSUN PROBLEMA perché quello che cerca è blocco sul A.classoggetto mentre nessun thread in possesso di questo tipo di serratura . Quindi, anche se hai dichiarato il metodo, in synchronizedrealtà si accede da due thread diversi ALLO STESSO TEMPO . Quindi: non usare mai riferimenti a oggetti per chiamare metodi statici
Alex Semeniuk,

113

Nell'esempio il metodo A e il metodo B sono metodi di istanza (al contrario dei metodi statici). Inserendo synchronizedun metodo di istanza significa che il thread deve acquisire il blocco (il "blocco intrinseco") sull'istanza dell'oggetto su cui viene chiamato il metodo prima che il thread possa iniziare l'esecuzione di qualsiasi codice in quel metodo.

Se hai due diversi metodi di istanza contrassegnati come sincronizzati e thread diversi chiamano questi metodi contemporaneamente sullo stesso oggetto, quei thread contenderanno lo stesso blocco. Una volta che un thread ottiene il blocco, tutti gli altri thread vengono esclusi da tutti i metodi di istanza sincronizzati su quell'oggetto.

Affinché i due metodi vengano eseguiti contemporaneamente, dovrebbero utilizzare blocchi diversi, in questo modo:

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

dove la sintassi del blocco sincronizzato consente di specificare un oggetto specifico di cui il thread in esecuzione deve acquisire il blocco intrinseco per accedere al blocco.

La cosa importante da capire è che anche se stiamo mettendo una parola chiave "sincronizzata" su singoli metodi, il concetto centrale è il blocco intrinseco dietro le quinte.

Ecco come il tutorial Java descrive la relazione:

La sincronizzazione si basa su un'entità interna nota come blocco intrinseco o blocco monitor. (Le specifiche API spesso si riferiscono a questa entità semplicemente come un "monitor".) I blocchi intrinsechi svolgono un ruolo in entrambi gli aspetti della sincronizzazione: imporre l'accesso esclusivo allo stato di un oggetto e stabilire relazioni che accadono prima che siano essenziali per la visibilità.

A ogni oggetto è associato un blocco intrinseco. Per convenzione, un thread che necessita di un accesso esclusivo e coerente ai campi di un oggetto deve acquisire il blocco intrinseco dell'oggetto prima di accedervi, quindi rilasciare il blocco intrinseco una volta terminato con essi. Si dice che un thread possiede il blocco intrinseco tra il momento in cui ha acquisito il blocco e rilasciato il blocco. Finché un thread possiede un blocco intrinseco, nessun altro thread può acquisire lo stesso blocco. L'altro thread si bloccherà quando tenta di acquisire il blocco.

Lo scopo del blocco è proteggere i dati condivisi. Si utilizzerebbero blocchi separati come mostrato nel codice di esempio sopra solo se ciascun blocco proteggesse membri di dati diversi.


quindi in questo esempio il blocco si trova sugli oggetti lockA \ lockB e non sulla classe A? È un esempio di blocco a livello di classe ?
Nimrod,

2
@Nimrod: si sta bloccando su lockA e su lockB oggetti e non sull'istanza di A. niente qui sta bloccando su una classe. blocco a livello di classe significherebbe ottenere il blocco su un oggetto di classe, usando qualcosa di simile static synchronizedosynchronized (A.class)
Nathan Hughes,

Ecco il link al tutorial di Java che spiega esattamente cosa si risponde qui.
Alberto de Paola,

18

Java Thread acquisisce un blocco a livello di oggetto quando entra in un metodo java sincronizzato di istanza e acquisisce un blocco a livello di classe quando entra in un metodo java sincronizzato statico .

Nel tuo caso, i metodi (istanza) sono della stessa classe. Quindi, ogni volta che un thread entra nel metodo o blocco java sincronizzato acquisisce un blocco (l'oggetto su cui viene chiamato il metodo). Pertanto, non è possibile richiamare contemporaneamente altri metodi sullo stesso oggetto fino al completamento del primo metodo e al rilascio del blocco (sull'oggetto).


se ho due thread su due diverse istanze della classe, allora saranno in grado di eseguire entrambi i metodi contemporaneamente in modo tale che un thread chiama un metodo sincronizzato e l'altro chiama il secondo metodo sincronizzato. Se la mia comprensione è corretta, allora posso usare private final Object lock = new object();con sincronizzato per consentire a un solo thread di eseguire uno dei due metodi? Grazie
Yug Singh,

13

Nel tuo caso hai sincronizzato due metodi sulla stessa istanza della classe. Quindi, questi due metodi non possono essere eseguiti simultaneamente su thread diversi della stessa istanza della classe A. Ma possono farlo su diverse istanze di classe A.

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

equivale a:

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

cosa succede se definisco un blocco come private final Object lock = new Object();e ora lo uso lockcon un blocco sincronizzato in due metodi, la tua affermazione sarà vera? IMO poiché Object è la classe padre di tutti gli oggetti, quindi anche se i thread si trovano su istanze diverse della classe, solo uno può accedere al codice all'interno del blocco sincronizzato alla volta. Grazie.
Yug Singh,

Se nella classe si definisce "blocco dell'oggetto finale privato" e si sincronizza con esso, si riempie il blocco per istanza della classe, quindi si comporterà come sincronizzato (questo).
Oleksandr_DJ

Sì, Object è un genitore per tutte le classi, ma l'istanza "lock" nel tuo caso è "istanza per classe proprietaria", quindi ha lo stesso effetto di "this" per la sincronizzazione.
Oleksandr_DJ

7

Dal collegamento alla documentazione di Oracle

Rendere sincronizzati i metodi ha due effetti:

In primo luogo, non è possibile interlacciare due invocazioni di metodi sincronizzati sullo stesso oggetto. Quando un thread esegue un metodo sincronizzato per un oggetto, tutti gli altri thread che invocano metodi sincronizzati per lo stesso blocco di oggetti (sospensione dell'esecuzione) fino a quando il primo thread non viene eseguito con l'oggetto.

In secondo luogo, quando esce un metodo sincronizzato, stabilisce automaticamente una relazione prima dell'evocazione successiva di un metodo sincronizzato per lo stesso oggetto. Ciò garantisce che le modifiche allo stato dell'oggetto siano visibili a tutti i thread

Questo risponderà alla tua domanda: sullo stesso oggetto, non puoi chiamare il secondo metodo sincronizzato quando è in corso l'esecuzione del primo metodo sincronizzato.

Dai un'occhiata a questa pagina di documentazione per comprendere i blocchi intrinseci e il comportamento dei blocchi.


6

Pensa al tuo codice come quello sotto:

class A {

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

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

Quindi, sincronizzato a livello di metodo significa semplicemente sincronizzato (questo). se un thread esegue un metodo di questa classe, otterrebbe il blocco prima di avviare l'esecuzione e lo manterrebbe fino al termine dell'esecuzione del metodo.

Ma posso eseguire methodB () su thread diversi mentre methodA () è ancora in esecuzione? (stesso oggetto)

Anzi, non è possibile!

Pertanto, più thread non saranno in grado di eseguire contemporaneamente un numero qualsiasi di metodi sincronizzati sullo stesso oggetto.


cosa succede se creo thread su due diversi oggetti della stessa classe? In questo caso, se chiamo un metodo da un thread e un altro metodo dal secondo thread, non verranno eseguiti contemporaneamente?
Yug Singh,

2
Lo faranno perché sono oggetti diversi. Cioè, se si desidera impedirlo, è possibile utilizzare metodi statici e sincronizzare la classe o utilizzare un oggetto variabile di classe come blocco o creare la classe Singleton. @Yug Singh
Khosro Makari

4

Con tutta chiarezza, è possibile che sia il metodo sincronizzato statico sia il metodo sincronizzato non statico possano essere eseguiti simultaneamente o contemporaneamente perché uno ha un blocco a livello di oggetto e un altro blocco a livello di classe.


3

L' idea chiave con la sincronizzazione che non affonda facilmente è che avrà effetto solo se i metodi vengono chiamati sullo stesso oggetto istanza - è già stato evidenziato nelle risposte e nei commenti -

Di seguito è riportato un programma di esempio per individuare chiaramente lo stesso -

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

Notare la differenza nell'output di come è consentito l'accesso simultaneo come previsto se vengono chiamati metodi su diverse istanze di oggetti.

Outut con noEffectOfSynchronizedAsMethodsCalledOnDifferentObjects () ha commentato - l'output è nell'ordine methodA in> methodA Out .. methodB in> methodB Out Ouput con * noEffectOfSynchronizedAsMethodsCalledOnDifferentObjects () * commentato

e Ouput con synchronizedEffectiveAsMethodsCalledOnSameObject () ha commentato - l'output mostra l'accesso simultaneo di methodA da Thread1 e Thread0 nella sezione evidenziata -

Ouput con * synchronizedEffectiveAsMethodsCalledOnSameObject () * ha commentato

Aumentare il numero di thread lo renderà ancora più evidente.


2

No, non è possibile, se fosse possibile, entrambi i metodi potrebbero aggiornare contemporaneamente la stessa variabile, danneggiando facilmente i dati.


2

Sì, possono eseguire contemporaneamente entrambi i thread. Se si creano 2 oggetti della classe poiché ogni oggetto contiene solo un blocco e ogni metodo sincronizzato richiede il blocco. Quindi, se si desidera eseguire contemporaneamente, creare due oggetti e quindi provare a eseguire utilizzando il riferimento a tali oggetti.


1

Lo stai sincronizzando sull'oggetto e non sulla classe. Quindi non possono correre contemporaneamente sullo stesso oggetto


0

Due thread diversi che eseguono un metodo sincronizzato comune sul singolo oggetto, poiché l'oggetto è lo stesso, quando un thread lo utilizza con il metodo sincronizzato, dovrà chiarire il blocco, se il blocco è abilitato, questo thread andrà in stato di attesa, se il blocco è disabilitato, può accedere all'oggetto, mentre accederà abiliterà il blocco e rilascerà il blocco solo al termine dell'esecuzione. quando arrivano gli altri thread, il blocco verrà chiarito, poiché è abilitato attenderà fino a quando il primo thread non avrà completato la sua esecuzione e rilascerà il blocco messo sull'oggetto, una volta rilasciato il blocco il secondo thread otterrà l'accesso all'oggetto e abiliterà il blocco fino alla sua esecuzione. quindi l'esecuzione non sarà simultanea, entrambi i thread verranno eseguiti uno per uno,


1
Per favore punteggia e capitalizza correttamente questo pasticcio. Non esiste una parola come "varify".
Marchese di Lorne,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.