2016-04-21 16 views
6

Da quello che ho capito il seguente codice non è efficiente:Dovrei cercare di evitare metodo sincronizzato static

class Foo { 
    static Resource resource1; 
    static Resource resource2; 

    static synchronized void methodA() { 
     resource1.add("abc"); 
    } 

    static synchronized void methodB() { 
     resource2.add("abc"); 
    } 
} 

Da quanto ho capito, entrambi i metodi bloccare in un singolo oggetto (l'oggetto di classe Foo.class), quindi suppongo che quanto segue sia una buona ottimizzazione?

class Foo { 
    static Resource resource1; 
    static Resource resource2; 

    static void methodA() { 
     synchronized(resource1) { 
      resource1.add("abc"); 
     } 
    } 

    static void methodB() { 
     synchronized(resource2) { 
      resource2.add("123"); 
     } 
    } 
} 

Fintanto che le due risorse non dipendono l'una dall'altra.

Quando devo considerare l'utilizzo del metodo sincronizzato statico?

risposta

7

Utilizzare il costrutto static synchronized quando la classe astrae l'accesso a un singolo critical resource, pertanto il blocco sulla classe è semanticamente corretto.

Se la classe astrae l'accesso a più di una risorsa critica, è necessario utilizzare un blocco più preciso, come nell'esempio.

È possibile considerare il modificatore synchronized per i metodi come zucchero sintattico, non c'è altra magia nera oltre al blocco sulla classe (o l'istanza se il metodo non è statico).

Nel primo esempio, è discutibile il motivo per cui una singola classe fornisce accesso a due diverse risorse critiche se non sono completamente correlate. Forse potresti spostare le sezioni critiche nelle stesse classi di risorse.

5

L'ottimizzazione è giusta.

Il primo blocco di codice sul Foo.class

Il secondo blocco di codice su due oggetti differenti: resource1 e resource2.

Visivamente si può immaginare questo

Primo codice:

Thread 1    Thread 2 
------------------------------ 

Foo.methodA() 

         Foo.methodB() 
// A call to methodB needs to wait for completion of methodA 

Secondo codice:

Thread 1    Thread 2 
------------------------------ 

Foo.methodA()   Foo.methodB()  
// At the same time in a machine with at least a dual core 

Si dovrebbe considerare di usare un metodo sincronizzato static solo quando si dispone di una risorsa per la sincronizzazione .

0

L'ottimizzazione è buona, ma essere consapevoli del possibile punto morto.Nel esempio, a volte si decide di accedere a entrambe le risorse:

class Foo { 
    static Resource resource1; 
    static Resource resource2; 

    static void methodA() { 
     synchronized(resource1) { 
      resource1.add("abc"); 
      synchronized(resource2) { 
       resource2.add("abc"); 
      } 
     } 
    } 

    static void methodB() { 
     synchronized(resource2) { 
      resource2.add("123"); 
      synchronized(resource1) { 
       resource1.add("123"); 
      } 
     } 
    } 
} 

Questo può portare a un punto morto:

  1. Discussione una prova per eseguire Methoda();
  2. Thread B tenta di eseguire methodB() allo stesso tempo;
  3. Thread A ottenere il blocco Resource1, Filo B ottenere Resource2 bloccare
  4. Discussione una prova per ottenere il blocco Resource2, e iniziare in attesa di blocco per rilasciare
  5. Discussione B cercare di ottenere il blocco Resource1, e iniziare in attesa di blocco per rilasciare
  6. Deadlock

per evitare questo, è possibile rendere la vostra classe di risorse per essere thread-safe:

class Resource { 
    private final Object mLock = new Object(); 
    ... 
    public void add(String str) { 
     synchronized(mLock) { 
      //do stuff 
     } 
    } 
} 
0

Il secondo metodo (il blocco degli oggetti) è preferibile in quanto offre un maggiore controllo su quando il blocco deve essere effettuato. Inoltre, impedisce a una parte esterna della classe di bloccare la classe indefinitamente, impedendo l'esecuzione dei propri metodi.

Si consideri il seguente: Immaginate un po 'di codice esterno contiene le seguenti affermazioni:

synchronized (Foo.class) { 
    Thread.sleep(10000); 
} 

Ora, se è stato utilizzato sincronizzato sui metodi di classe stessi come nel metodo 1, le altre classi contemporaneamente cercando di chiamare Methoda o MethodB saranno essere bloccato fino al termine del sonno. Se tuttavia hai usato il blocco interno sugli oggetti interni come nel metodo 2, le altre classi non dovranno aspettare.

causa di quanto sopra, non avrei davvero consigliare metodo 1.

PS Ho appena notato che le serrature interne nel metodo 2 non sono stati dichiarati finale. Questo sarà un problema se vengono riassegnati mentre un metodo è occupato a bloccarli (il blocco sarà quindi su un'istanza differente). Per evitare ciò, piuttosto dichiararli finali come segue:

final static Resource resource1 = new Resource(...); 
final static Resource resource2 = new Resource(...); 

In breve, non sincronizzare mai su oggetti non finali.

Problemi correlati