Corrección multihilo: Utilización de bloqueo sincronizado

Estoy utilizando la biblioteca CMU Sphinx Speech Recognizer ( Link to source ) que hace uso de bloques synchronized .

Un bloque de ejemplo de RecognizerTask:

 Event mailbox; [...] public void start() { synchronized (this.mailbox) { this.mailbox.notifyAll(); this.mailbox = Event.START; } } 

El código funciona sin ningún problema, sin embargo BugFinder da esta advertencia:

Bug: Sincronización en RecognizerTask.mailbox en inútil intento de protegerlo

Este método se sincroniza en un campo en lo que parece ser un intento de protegerse contra actualizaciones simultáneas a ese campo. Pero proteger un campo obtiene un bloqueo en el objeto referenciado, no en el campo. Esto puede no proporcionar la exclusión mutua que necesita y otros subprocesos pueden obtener bloqueos en los objetos referenciados (para otros propósitos). Un ejemplo de este patrón sería:

 private Long myNtfSeqNbrCounter = new Long(0); private Long getNotificationSequenceNumber() { Long result = null; synchronized(myNtfSeqNbrCounter) { result = new Long(myNtfSeqNbrCounter.longValue() + 1); myNtfSeqNbrCounter = new Long(result.longValue()); } return result; } 

Para ser honesto, no entiendo la descripción del error y lo que se supone que está mal en este caso. ¿Es una variable global no un campo? Y si no, ¿cómo puedo mejorar el código?

/ Edit: Esta es la única parte donde Event.wait() se llama:

 Event todo = Event.NONE; synchronized (this.mailbox) { todo = this.mailbox; /* If we're idle then wait for something to happen. */ if (state == State.IDLE && todo == Event.NONE) { try { //Log.d(getClass().getName(), "waiting"); this.mailbox.wait(); todo = this.mailbox; //Log.d(getClass().getName(), "got" + todo); } catch (InterruptedException e) { /* Quit main loop. */ //Log.e(getClass().getName(), "Interrupted waiting for mailbox, shutting down"); todo = Event.SHUTDOWN; } } /* Reset the mailbox before releasing, to avoid race condition. */ this.mailbox = Event.NONE; } 

Este código también utiliza una sentencia synchronized . ¿Tiene sentido en absoluto utilizarlo?

2 Solutions collect form web for “Corrección multihilo: Utilización de bloqueo sincronizado”

No creo que se aplique en su caso. Tienes una llamada a notifyAll() que significa que en algún lugar del código de los otros hilos hay una llamada wait() coincide:

 synchronized (this.mailbox) { this.mailbox.wait(); } 

Lo que significa que el otro hilo renunciará al bloqueo mientras espera ser notificado.

Su inspector de código probablemente está confundido por la línea:

 this.mailbox = Event.START; 

Lo que significa que puede estar modificando simultáneamente este objeto, de modo que si otro hilo intenta obtener el bloqueo en this.mailbox , verá un objeto diferente. Sin embargo, creo que desde entonces:

  1. this.mailbox es visible globalmente
  2. Asignaciones de referencias son atómicas
  3. La cerradura genera una valla

Todos los subprocesos deben tener una vista actualizada del objeto de sincronización en todo momento.

El bloque sincronizado "captura" el bloqueo para el objeto dado, en su caso para el objeto denotado por el mailbox . Una vez que cambie el mailbox variables para que apunte a un objeto diferente, otros hilos pueden "capturar" el bloqueo para este objeto sin ningún problema, ya que no se toma.

Tenga en cuenta que el bloqueo es para objetos, y no para referencias!

Ahora, concider el siguiente [pseudo código]:

 synchronised (myObject) { myObject = new Object(); i += 5; //assume i is an instance variable } 

Prácticamente no hay cerradura aquí! Cada subproceso está creando un nuevo objeto en el bloque de bloqueo, y la modificación de i no está sincronizada!

FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.