¿Cómo se garantiza que varios subprocesos puedan acceder de forma segura a un campo de clase?

StackOverflow https://stackoverflow.com/questions/118371

Pregunta

Cuando varios subprocesos acceden a un campo de clase a través de un método getter, ¿cómo se mantiene la seguridad de los subprocesos?¿Es suficiente la palabra clave sincronizada?

¿Es esto seguro?

public class SomeClass {
    private int val;

    public synchronized int getVal() {
        return val;
    }

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

¿O el colocador introduce más complicaciones?

¿Fue útil?

Solución

Si aquí también usa 'sincronizado' en el configurador, este código es seguro para subprocesos.Sin embargo, puede que no sea lo suficientemente granular;Si tiene 20 captadores y definidores y todos están sincronizados, es posible que esté creando un cuello de botella en la sincronización.

En este caso específico, con una única variable int, eliminar 'sincronizado' y marcar el campo int como 'volatile' también garantizará la visibilidad (cada hilo verá el último valor de 'val' al llamar al captador), pero es posible que no sincronizarse lo suficiente para sus necesidades.Por ejemplo, esperando

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

establecer val en 2 si y solo si ya es 1 es incorrecto.Para esto necesita un bloqueo externo o algún método atómico de comparación y configuración.

Te recomiendo encarecidamente que leas Concurrencia de Java en la práctica por Brian Goetz et al, tiene la mejor cobertura de las construcciones de concurrencia de Java.

Otros consejos

Además de comentario de cowan, puede hacer lo siguiente para comparar y almacenar:

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

Esto funciona porque el bloqueo definido mediante un método sincronizado es implícitamente el mismo que el bloqueo del objeto (ver especificaciones del lenguaje java).

Según tengo entendido, debería utilizar sincronizado tanto en el método getter como en el setter, y eso es suficiente.

Editar:Aquí hay un enlace a más información sobre sincronización y otras cosas.

Si su clase contiene sólo una variable, entonces otra forma de lograr la seguridad de subprocesos es utilizar el objeto AtomicInteger existente.

public class ThreadSafeSomeClass {

    private final AtomicInteger value = new AtomicInteger(0);

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

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

}

Sin embargo, si agrega variables adicionales que sean dependientes (el estado de una variable depende del estado de otra), AtomicInteger no funcionará.

Haciéndose eco de la sugerencia de leer "La simultaneidad de Java en la práctica".

Para objetos simples esto puede ser suficiente.En la mayoría de los casos, debe evitar la palabra clave sincronizada porque puede encontrarse con un punto muerto en la sincronización.

Ejemplo:

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

Garantiza que solo un subproceso lea o escriba en el miembro de la instancia local.

Lea el libro "Programación concurrente en Java (tm):Principios y patrones de diseño (Java (Addison-Wesley))", tal vez http://java.sun.com/docs/books/tutorial/essential/concurrency/index.html también es útil...

La sincronización existe para proteger contra interferencias de subprocesos y errores de coherencia de la memoria.Al sincronizar en getVal(), el código garantiza que otros métodos sincronizados en SomeClass no se ejecuten al mismo tiempo.Como no existen otros métodos sincronizados, no proporciona mucho valor.También tenga en cuenta que las lecturas y escrituras en primitivas tienen acceso atómico.Esto significa que con una programación cuidadosa, no es necesario sincronizar el acceso al campo.

Leer Sincronización.

No estoy seguro de por qué esto se redujo a -3.Simplemente estoy resumiendo lo que dice el tutorial de sincronización de Sun (así como mi propia experiencia).

El uso de acceso de variable atómica simple es más eficiente que acceder a estas variables a través del código sincronizado, pero requiere más cuidado del programador para evitar errores de consistencia de la memoria.Si el esfuerzo adicional vale la pena depende del tamaño y la complejidad de la aplicación.

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