Domanda

Durante la discussione di una sincronizzazione Java domanda , qualcuno ha commentato che i seguenti frammenti non sono equivalenti (e possono essere compilati con diversi bytecode):

public synchronized void someMethod() {
  //stuff
}

e

public void someMethod() {
  synchronized (this) {
    //stuff
  }
}

Sono equivalenti?

È stato utile?

Soluzione

Sono equivalenti nella funzione, sebbene i compilatori che ho testato (Java 1.6.0_07 ed Eclipse 3.4) generino un bytecode diverso. Il primo genera:

// access flags 33
public synchronized someMethod()V
  RETURN

Il secondo genera:

// access flags 1
public someMethod()V
  ALOAD 0
  DUP
  MONITORENTER
  MONITOREXIT
  RETURN

(Grazie a ASM per la stampa del bytecode).

Quindi la differenza tra loro persiste a livello di bytecode, e spetta alla JVM rendere lo stesso comportamento. Tuttavia, hanno lo stesso effetto funzionale: vedi esempio nella specifica del linguaggio Java.

Va ??notato che se il metodo viene sovrascritto in una sottoclasse, non è necessariamente sincronizzato - quindi non vi è alcuna differenza nemmeno a tale riguardo.

Ho anche eseguito un test per bloccare un thread provando ad accedere al monitor in ogni caso per confrontare come sarebbero state le loro tracce dello stack in un dump del thread ed entrambi contenevano il metodo in questione, quindi non c'è alcuna differenza.

Altri suggerimenti

Ho fatto il commento originale che le dichiarazioni sono identiche.

In entrambi i casi, la prima cosa che succede è che il thread chiamante tenterà di acquisire il monitor dell'oggetto corrente (che significa, this ').

Non conosco diversi bytecode, sarò felice di sentire la differenza. Ma in pratica sono identici al 100%.

EDIT: ho intenzione di chiarire questo dato che alcune persone qui hanno sbagliato. Prendere in considerazione:

public class A {
    public synchronized void doStuff()
    {
        // do stuff
    }
}

public class B extends A {
    public void doStuff()
    {
        // do stuff
        // THIS IS OVERRIDE!
    }
}

In questo caso doStuff () nella classe B sostituisce ancora doStuff () nella classe A anche se non è sincronizzato.

La parola chiave sincronizzata non fa mai parte del contratto! Non per sottoclassi, non per interfacce, non per classi astratte.

Ho fatto il commento originale. Il mio commento è stato che sono logicamente equivalenti, ma compilano con bytecode diverso .

Non ho aggiunto nient'altro per giustificarlo in quel momento perché non c'è molto da giustificare davvero - semplicemente fanno compilano in diversi bytecode. Se si dichiara un metodo come sincronizzato , tale sincronizzazione fa parte della definizione del metodo. Un blocco sincronizzato all'interno di un metodo non è parte della definizione del metodo , ma comporta invece codici secondari separati per acquisire e rilasciare il monitor, come ha illustrato uno dei poster sopra. A rigor di termini, sono cose leggermente diverse, sebbene alla logica generale del tuo programma , sono equivalenti .

Quando è importante? Bene, sulla maggior parte delle macchine virtuali desktop moderne, quasi mai. Ma per esempio:

  • una VM potrebbe in linea di principio effettuare ottimizzazioni in un caso ma non nell'altro
  • ci sono alcune ottimizzazioni del compilatore JIT in cui il numero di bytecode nel metodo viene preso come criterio per quali ottimizzazioni effettuare
  • una VM senza un compilatore JIT (certamente pochi al giorno d'oggi, ma forse su un dispositivo mobile più vecchio?) avrà più bytecode da elaborare su ogni chiamata

Sì. L'uso della parola chiave sincronizzata su un metodo di istanza utilizza "this" come monitor, mentre anche su un metodo di classe (metodo statico) utilizza l'oggetto di classe "Class (Foo.class).

In questo modo puoi sincronizzare interi metodi e, allo stesso tempo, sincronizzarlo con uno snippet di codice in un altro metodo usando lo stile del blocco sincronizzato.

Non riesco a vedere alcuna differenza funzionale - entrambi sincronizzano i loro interi corpi di metodo su (questo). In che modo la persona che ha commentato che questi sono diversi ha giustificato la loro affermazione?

Non sono abbastanza equivalenti nella funzione. Un altro codice potrebbe usare la riflessione per vedere se il tuo metodo ha il modificatore sincronizzato, ma non c'è modo di dire se un metodo contiene un blocco sincronizzato senza leggere il suo bytecode.

La possibilità di determinare se un metodo viene sincronizzato di tanto in tanto è utile. Personalmente, ho usato quella bandiera per evitare il blocco ridondante durante la sincronizzazione nella programmazione orientata all'aspetto.

MyObject myObjectA;
MyObject myObjectB;

public void someMethod() {
  synchronized (this) {
    //stuff
  }
}

public void someMethodA() {
  synchronized (myObjectA) {
    //stuff
  }
}

public void someMethodB() {
  synchronized (myObjectB) {
    //stuff
  }
}

In questo caso:

  • someMethod blocca l'intera classe
  • someMethodA blocca solo myObjectA
  • someMethodB blocca solo myObjectB
  • someMethodA e someMethodB possono essere invocati contemporaneamente
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top