2012-06-25 15 views
5

Estoy confundido acerca de la sincronización de un método de instancia y un método estático. Quiero escribir una clase segura hilo como sigue:Sincronización en el método estático y de instancia

public class safe { 

    private final static ConcurrentLinkedQueue<Object> objectList= 
     new ConcurrentLinkedQueue<Object>(); 

    /** 
    * retrieves the head of the object and prints it 
    */ 
    public synchronized static void getHeadObject() { 
     System.out.println(objectList.peek().toString()); 

    } 

    /** 
    * creates a new object and stores in the list. 
    */ 
    public synchronized void addObject() { 
     Object obj=new Object(); 
     objectList.add(obj); 

    } 
} 

Sincronización en un método estático se bloqueará en safe.class cerradura y la sincronización en un método de instancia se bloqueará en este .y, por tanto, se alcanzará un estado incoherente .

Si quiero lograr un estado consistente para un fragmento de código a continuación, ¿cómo se puede lograr eso?

+1

Por qué es 'addObject' un método de instancia? ¿Por qué no estático? ¿Por qué estás sincronizando alrededor de un objeto concurrente de todos modos? Ellos ya son enhebrables. – Wug

+0

¿Qué quiere decir con estado inconsistente? –

+0

Estoy asumiendo que quería decir 'cola ObjectList = nueva lista de ' en lugar de 'ConcurrentLinkedQueue ObjectList = new ConcurrentLinkedQueue ' por lo que obtener una respuesta a la pregunta real que está pidiendo. –

Respuesta

1

EDIT: Supongo que se refería a Queue<Object> objectList en lugar de ConcurrentLinkedQueue<Object> objectList. ConcurrentLinkedQueue<Object> ya hace toda su seguridad de hilo para usted, lo que significa que puede llamar al objectList.peek() todo lo que quiera sin preocuparse por las condiciones de carrera. Esto es excelente si está desarrollando programas de subprocesos múltiples, pero no tan buenos para aprender sobre seguridad de subprocesos.

Sus métodos no necesitan ser synchronized, suponiendo que tiene el funcionamiento de una rosca en una instancia del objeto a la vez, pero sin embargo, si es necesario tener varias instancias de la clase que todos se refieren a la misma variable de clase estática, tienen que synchronized sobre la variable de clase, así:

public static void getHeadObject() { 
    synchronized(safe.objectList) { 
     System.out.println(objectList.peek().toString()); 
    } 
} 

Esto bloquea el objectList y no permite que se pueda leer o escribir en cualquier otro hilo tan pronto como el programa se encuentra dentro del bloque de sincronización. Haga lo mismo para todos los demás métodos que sean synchronized.

NOTA:

Sin embargo, puesto que está haciendo sólo una operación de obtención sencilla List.peek(), que realmente no es necesario sincronizar a través de la objectList ya que en una condición de carrera, este obtendrá un valor de el List u otro. El problema con las condiciones de carrera es cuando se realizan múltiples operaciones complejas de lectura/escritura, y el valor cambia entre ellas.

Por ejemplo, si usted tenía una clase PairInt con PairInt.x y PairInt.y campos, con la restricción de que x = 2y, y que quería hacer

System.out.println(myIntPair.x.toString() + ", " + myIntPair.y.toString()); 

y otro hilo fue la actualización del valor de x y y en al mismo tiempo,

myIntPair.y = y + 3; 
myIntPair.x = 2 * y; 

Y la escritura hilo modificado myIntPair en medio de su hilo de lectura myIntPair.x.toString() y myIntPair.y.toString() puede obtener un resultado que se parece a (10, 8), lo que significa que si está operando bajo el supuesto de que x == 2 * y podría bloquear su programa.

En ese caso, su lectura tiene que utilizar un synchronized, pero para cosas más simples como peek() en un simple object que se añaden o se eliminan, sin modificar, mientras que en la cola, el synchronized puede, en la mayoría de los casos ser dropeado De hecho, para string, int, bool, y similares, se debe descartar la condición synchronized para una lectura simple.

Sin embargo, las escrituras siempre deben ser synchronized en operaciones que no son explícitamente seguras para subprocesos, es decir, que ya las maneja Java. Y en cuanto a adquirir más de un recurso, o requiere que el recurso de suspender el mismo durante toda la operación como lo hace varias líneas de lógica en ello, entonces DEBE UTILIZARsynchronized

+0

Él ya usa la palabra clave sincronizada en la declaración de 'getHeadObject', que simplemente se sincroniza en safe.class. – Wug

+0

Creo que Hans es correcto porque debemos ser muy cuidadosos en tales escenarios, cualquier método de instancia sincronizada debe tener un bloque sincronizado explícito para acceder a campos estáticos si los campos estáticos requieren un acceso exclusivo. –

+0

Eso no ayuda para nada ... dos objetos seguros accederán a la misma lista de objetos, y si está sincronizando sobre ambos objetos, 'sincronizado' le permitirá escribir con ambos al mismo tiempo, porque usted no está usar explícitamente (en la medida en que vm pueda decirlo), el mismo objeto. –

2

En primer lugar, ConcurrentLinkedQueue no requiere explícita sincronización. Ver this answer.

En segundo lugar, siempre se puede sincronizar objeto se está accediendo:

public class safe { 

     private final static ConcurrentLinkedQueue<Object> objectList= 
      new ConcurrentLinkedQueue<Object>(); 

     /** 
     * retrieves the head of the object and prints it 
     */ 
    public static void getHeadObject() { 
     synchronized(objectList){ 
      System.out.println(objectList.peek().toString()); 
     } 

    } 

     /** 
     * creates a new object and stores in the list. 
     */ 
    public void addObject() { 
      Object obj=new Object(); 
     synchronized(objectList){ 
      objectList.add(obj); 
     } 

    } 
} 
+0

@assylias: "copiar y pegar", lo siento =) –

0

Algunos comentarios:

  • convenciones de Java:
    • nombres de clase debe estar en CamelCase (es decir, llame a su clase Safe, no safe)
    • static viene antes synchronized en la declaración métodos
    • static viene antes final en la declaración campos
  • como ya se ha dicho, ConcurrentLinkedQueue ya está seguro para subprocesos, así que no hay necesidad de sincronización en el ejemplo que das.
  • mezcla de métodos estáticos y no estáticos la forma en que lo hace se ve raro.
  • asumiendo que su caso de uso real es más complicado y necesita un método para ejecutar operaciones atómicas, entonces su código no funciona, como señaló, porque los 2 métodos sincronizados no se sincronizan en el mismo monitor:
public static synchronized getHeadObject(){} //monitor = Safe.class 
public static synchronized addObject(){} //monitor = this 

Así que para responder a su pregunta específica, se puede utilizar un objeto estático separado como un bloqueo:

public class Safe { 

    private static final ConcurrentLinkedQueue<Object> objectList = 
      new ConcurrentLinkedQueue<Object>(); 
    // lock must be used to synchronize all the operations on objectList 
    private static final Object lock = new Object(); 

    /** 
    * retrieves the head of the object and prints it 
    */ 
    public static void getHeadObject() { 
     synchronized (lock) { 
      System.out.println(objectList.peek().toString()); 
     } 
    } 

    /** 
    * creates a new object and stores in the list. 
    */ 
    public void addObject() { 
     synchronized (lock) { 
      Object obj = new Object(); 
      objectList.add(obj); 
     } 
    } 
} 
Cuestiones relacionadas