Domanda

Quando si accede a un campo di classe tramite un metodo getter da più thread, come si mantiene la sicurezza dei thread? La parola chiave sincronizzata è sufficiente?

È sicuro:

public class SomeClass {
    private int val;

    public synchronized int getVal() {
        return val;
    }

    private void setVal(int val) {
        this.val = val;
    }
}

o il setter presenta ulteriori complicazioni?

È stato utile?

Soluzione

Se si utilizza 'sincronizzato' anche sul setter qui, questo codice è thread-safe. Tuttavia, potrebbe non essere sufficientemente granulare; se hai 20 getter e setter e sono tutti sincronizzati, potresti creare un collo di bottiglia per la sincronizzazione.

In questa specifica istanza, con una singola variabile int, quindi l'eliminazione del 'sincronizzato' e la marcatura del campo int 'volatile' garantiranno anche la visibilità (ogni thread vedrà l'ultimo valore di 'val' quando chiama il getter) ma potrebbe non essere abbastanza sincronizzato per le tue esigenze. Ad esempio, in attesa

 int old = someThing.getVal();
 if (old == 1) {
    someThing.setVal(2);
 }

per impostare val su 2 se e solo se è già 1 non è corretto. Per questo è necessario un blocco esterno o un metodo di confronto e impostazione atomico.

Consiglio vivamente di leggere Java Concurrency In Practice di Brian Goetz et al , ha la migliore copertura dei costrutti di concorrenza di Java.

Altri suggerimenti

Oltre a Commento di Cowan , potresti fare quanto segue per confrontare e archiviare:

synchronized(someThing) {
    int old = someThing.getVal();
    if (old == 1) {
        someThing.setVal(2);
    }
}

Funziona perché il blocco definito tramite un metodo sincronizzato è implicitamente lo stesso del blocco dell'oggetto ( vedi le specifiche della lingua java ).

Da quanto ho capito dovresti usare sincronizzato sia sui metodi getter che setter, e questo è sufficiente.

Modifica: ecco un link a qualche informazione in più sulla sincronizzazione e cosa no.

Se la tua classe contiene solo una variabile, un altro modo per ottenere thread-safety è usare l'oggetto AtomicInteger esistente.

public class ThreadSafeSomeClass {

    private final AtomicInteger value = new AtomicInteger(0);

    public void setValue(int x){
         value.set(x);
    }

    public int getValue(){
         return value.get();
    }

}

Tuttavia, se si aggiungono ulteriori variabili in modo tale che siano dipendenti (lo stato di una variabile dipende dallo stato di un'altra), AtomicInteger non funzionerà.

Facendo eco al suggerimento di leggere " Java Concurrency in Practice " ;.

Per oggetti semplici questo può essere sufficiente. Nella maggior parte dei casi dovresti evitare la parola chiave sincronizzata perché potresti imbatterti in un deadlock di sincronizzazione.

Esempio:

public class SomeClass {

  private Object mutex = new Object();
  private int    val   = -1; // TODO: Adjust initialization to a reasonable start
                             //       value
  public int getVal() {
    synchronized ( mutex ) {
      return val;
    }
  }

  private void setVal( int val ) {
    synchronized ( mutex ) {
      this.val = val;
    }
  }
}

Assicura che solo un thread legge o scrive nel membro dell'istanza locale.

Leggi il libro " Concurrent Programming in Java (tm): Principi e modelli di progettazione (Java (Addison-Wesley)) " ;, forse http://java.sun.com/docs/books/tutorial/essential/concurrency/index.html è anche utile. .

Esiste la sincronizzazione per proteggere dalle interferenze del thread e dagli errori di coerenza della memoria. Sincronizzando su getVal (), il codice garantisce che anche altri metodi sincronizzati su SomeClass non vengano eseguiti contemporaneamente. Poiché non ci sono altri metodi sincronizzati, non fornisce molto valore. Si noti inoltre che le letture e le scritture su primitive hanno accesso atomico. Ciò significa che con un'attenta programmazione, non è necessario sincronizzare l'accesso al campo.

Leggi Sicronizzazione .

Non sono proprio sicuro del perché questo sia stato portato a -3. Sto semplicemente riassumendo ciò che dice il tutorial di sincronizzazione di Sun (così come la mia esperienza).

  

L'uso dell'accesso variabile atomico semplice è   più efficiente dell'accesso a questi   variabili tramite codice sincronizzato,   ma richiede maggiore cura da parte del   programmatore per evitare la coerenza della memoria   errori. Se lo sforzo extra è   vale la pena dipende dalla dimensione e   complessità dell'applicazione.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top