Pregunta

¿En qué casos es necesario sincronizar el acceso a los miembros de la instancia? Entiendo que el acceso a los miembros estáticos de una clase siempre debe sincronizarse, ya que se comparten en todas las instancias de objetos de la clase.

Mi pregunta es ¿cuándo sería incorrecto si no sincronizo los miembros de la instancia?

por ejemplo si mi clase es

public class MyClass {
    private int instanceVar = 0;

    public setInstanceVar()
    {
        instanceVar++;
    }

    public getInstanceVar()
    {
        return instanceVar;
    }
}

en qué casos (de uso de la clase MyClass ) necesitaría tener métodos: setInstanceVar () público sincronizado y público sincronizado getInstanceVar () ?

Gracias de antemano por sus respuestas.

¿Fue útil?

Solución

Depende de si quieres que tu clase sea segura para subprocesos. La mayoría de las clases no deberían ser seguras para subprocesos (por simplicidad) en cuyo caso no necesita sincronización. Si necesita que sea seguro para subprocesos, debe sincronizar el acceso o para que la variable sea volátil. (Evita que otros hilos obtengan datos "obsoletos").

Otros consejos

El modificador sincronizado es realmente una idea mala y debe evitarse a toda costa. Creo que es encomiable que Sun haya intentado que el bloqueo sea un poco más fácil de lograr, pero sincronizado solo causa más problemas de los que vale.

El problema es que un método sincronizado en realidad es solo sintaxis de azúcar para obtener el bloqueo en this y mantenerlo durante todo el método. Por lo tanto, public sincronizado void setInstanceVar () sería equivalente a algo como esto:

public void setInstanceVar() {
    synchronized(this) {
        instanceVar++;
    }
}

Esto es malo por dos razones:

  • Todos los métodos sincronizados dentro de la misma clase utilizan exactamente el mismo bloqueo, lo que reduce el rendimiento
  • Cualquiera puede acceder a la cerradura, incluidos los miembros de otras clases.

No hay nada que me impida hacer algo así en otra clase:

MyClass c = new MyClass();
synchronized(c) {
    ...
}

Dentro de ese bloque sincronizado , mantengo el bloqueo que requieren todos los métodos sincronizados dentro de MyClass . Esto reduce aún más el rendimiento y dramáticamente aumenta las posibilidades de un punto muerto.

Un mejor enfoque es tener un objeto lock dedicado y usar el bloque sincronizado (...) directamente:

public class MyClass {
    private int instanceVar;
    private final Object lock = new Object();     // must be final!

    public void setInstanceVar() {
        synchronized(lock) {
            instanceVar++;
        }
    }
}

Alternativamente, puede usar la interfaz java.util.concurrent.Lock y la implementación java.util.concurrent.locks.ReentrantLock para lograr básicamente el mismo resultado ( de hecho, es lo mismo en Java 6).

Si desea hacer que este hilo de clase sea seguro, declararía instanceVar como volatile para asegurarme de que siempre obtenga el valor más actualizado de la memoria y también haría el setInstanceVar () sincronizado porque en la JVM un incremento no es una operación atómica.

private volatile int instanceVar =0;

public synchronized setInstanceVar() { instanceVar++;

}

. Aproximadamente, la respuesta es "depende". Sincronizar su setter y getter aquí solo tendría el propósito de garantizar que múltiples hilos no pudieran leer las variables entre las operaciones de incremento de cada uno:

 synchronized increment()
 { 
       i++
 }

 synchronized get()
 {
   return i;
  }

pero eso realmente ni siquiera funcionaría aquí, porque para asegurarse de que el hilo de la persona que llama tenga el mismo valor que incrementó, tendría que garantizar que está aumentando atómicamente y luego recuperando, lo que no está haciendo aquí - es decir, tendrías que hacer algo como

  synchronized int {
    increment
    return get()
  }

Básicamente, la sincronización es útil para definir qué operaciones deben garantizarse para ejecutar threadsafe (en otras palabras, no puede crear una situación en la que un hilo separado socave su operación y haga que su clase se comporte de manera ilógica, o socava lo que espera que el estado de los datos a ser). En realidad, es un tema más grande del que se puede abordar aquí.

Este libro La concurrencia de Java en la práctica es excelente, y ciertamente mucho más confiable que yo.

Para decirlo simplemente, se usa sincronizado cuando tiene múltiples hilos que acceden al mismo método de la misma instancia que cambiará el estado del objeto / aplicación.

Se entiende como una forma simple de evitar condiciones de carrera entre subprocesos, y realmente solo debe usarlo cuando planea tener subprocesos concurrentes que acceden a la misma instancia, como un objeto global.

Ahora, cuando esté leyendo el estado de una instancia de un objeto con hilos concurrentes, es posible que desee ver java.util.concurrent.locks.ReentrantReadWriteLock, que en teoría permite leer muchos hilos a la vez , pero solo se permite escribir un hilo. Entonces, en el ejemplo del método getter y setting que todo el mundo parece estar dando, podría hacer lo siguiente:

public class MyClass{
    private ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
    private int myValue = 0;

    public void setValue(){
        rwl.writeLock().lock();
        myValue++;
       rwl.writeLock().unlock();
    }

    public int getValue(){
       rwl.readLock.lock();
       int result = myValue;
       rwl.readLock.unlock();
       return result;
    }
}

En Java, las operaciones en ints son atómicas, así que no, en este caso no necesita sincronizar si todo lo que está haciendo es 1 escritura y 1 lectura a la vez.

Si estos fueron largos o dobles, es necesario sincronizar porque es posible que parte del largo / doble se actualice, luego se lea otro hilo, y finalmente la otra parte del largo / doble se actualice.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top