2011-01-21 7 views
5

tengo las siguientes clases en Javabucle de sincronización estancamiento

public class Counter { 
    private int value; 

    public Counter(int value) { 
     this.value = value; 
    } 
    public void setValue(int value) { 
     this.value = value; 
    } 
    public void decrement() { 
     this.value--; 
    } 
    public int getValue() { 
     return this.value; 
    } 
} 

public class Cell extends Thread { 

    private Object sync; 
    private Counter counter; 

    public Cell(Object sync, Counter counter) { 
     this.sync = sync; 
     this.counter = counter; 
    } 

    public void run() { 
     for (int r=0; r<Simulation.ROUND_NUM; r++) { 

      // do something 

      synchronized(counter) { 
       counter.decrement(); 
       counter.notifyAll(); 
      } 
      synchronized(sync) { 
       try { 
        sync.wait(); 
       } 
       catch (Exception ex) {} 
      } 

     } 
    } 
} 

public class Simulation extends Thread { 

    public static final int THREAD_NUM = 5; 
    public static final int ROUND_NUM = 5; 

    public Object sync = new Object(); 
    private Counter counter = new Counter(THREAD_NUM); 

    public void run() { 

     for (int i=0; i<THREAD_NUM; i++) { 
      Cell c = new Cell(sync,counter); 
      c.start(); 
     } 

     for (int i=0; i<ROUND_NUM; i++) { 
      synchronized(counter) { 
       while(counter.getValue() != 0) { 
        try { 
         counter.wait(); 
        } 
        catch (Exception ex) {} 
       } 
       counter.setValue(THREAD_NUM); 
      } 

      synchronized(sync) { 
       sync.notifyAll(); 
      } 
     } 
    } 
} 

El objetivo es evitar que se ejecute la siguiente iteración del bucle en cada hilo de la célula, hasta que cada hilo de la célula se llevará a cabo en cada iteración. Mi solución a veces conduce a un punto muerto. No puedo entender por qué. Por favor, ayuda

+2

Nota al margen: si no necesita explícitamente utilizar 'Thread',' wait' y 'notify' (y está utilizando Java5 o posterior), sería mejor que tuviera un [' CountDownLatch'] (http : //download.oracle.com/javase/6/docs/api/java/util/concurrent/CountDownLatch.html) en su lugar. –

Respuesta

3

En su código, no parece haber ninguna garantía de que cuando se ejecuta sync.notifyAll(), todos los hilos de la celda llegaron al sync.wait(). Esto se refiere al último hilo de celda (el quinto en su ejemplo) que necesita agarrar el candado para sync para esperarlo. Pero el hilo de simulación también intenta lo mismo sin asegurarse de que todos estén esperando. Esa condición de carrera hace que la Simulación a veces tome el bloqueo antes de que la última Celda pueda hacer lo mismo y espere.

Dado que la última celda no está esperando, no se notifica por lo que todo se bloquea. Puede probar esto mediante la adición de un System.out.println() como la primera línea en cada bloque synchronized (sync) y escribir "en espera de sincronización" y "notificar sincronización" en consecuencia. Verá que solo 4 subprocesos están esperando sincronizarse cuando lo notifique.

Para asegurarse de que todo el mundo está esperando cuando las notifica simulador, tienen los dos bloques sincronizados en Cell#run() anidados:

public class Counter { 
    private int value; 

    public Counter(int value) { 
     this.value = value; 
    } 

    public void setValue(int value) { 
     this.value = value; 
    } 

    public void decrement() { 
     this.value--; 
    } 

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

    public static void main(String[] args) { 
     new Simulation().start(); 
    } 
} 

class Cell extends Thread { 

    private Object sync; 
    private Counter counter; 

    public Cell(Object sync, Counter counter) { 
     this.sync = sync; 
     this.counter = counter; 
    } 

    public void run() { 
     for (int r = 0; r < Simulation.ROUND_NUM; r++) { 

      // do something 

      synchronized (sync) { 
       synchronized (counter) { 
        counter.decrement(); 
        counter.notifyAll(); 
       } 
       try { 
        sync.wait(); 
       } catch (Exception ignored) {} 
      } 


     } 
    } 
} 

class Simulation extends Thread { 

    public static final int THREAD_NUM = 900; 
    public static final int ROUND_NUM = 30; 

    public Object sync = new Object(); 
    private Counter counter = new Counter(THREAD_NUM); 

    public void run() { 

     for (int i = 0; i < THREAD_NUM; i++) { 
      Cell c = new Cell(sync, counter); 
      c.start(); 
     } 

     for (int i = 0; i < ROUND_NUM; i++) { 
      synchronized (counter) { 
       while (counter.getValue() != 0) { 
        try { 
         counter.wait(); 
        } catch (Exception ex) { 
        } 
       } 
       counter.setValue(THREAD_NUM); 
      } 

      synchronized (sync) { 
       sync.notifyAll(); 
      } 
     } 
    } 
} 
+0

Eso definitivamente funciona mejor, pero a pesar de los hilos que creo, más programas sin terminar obtengo. Teniendo en cuenta 900 hilos y 30 repeticiones, solo 2 de mis 10 ensayos recientes se finalizaron con éxito. – marooou

+0

No veo por qué te meterías en un callejón sin salida ... ¿El código que publicaste es el código exacto que estás ejecutando? –

+0

No es el código exacto. En mi código, el comentario se reemplaza con algunas acciones, pero cuando las elimino en la clase Cell, el problema no desaparece. Y lo que es aún más extraño, cuando me pongo a dormir en lugar de comentar, el programa funciona perfectamente ... siempre. – marooou

5

En primer lugar, puede hacer uso de la clase AtomicInteger en lugar de la clase de contador que ha realizado. La clase AtomicInteger es segura para subprocesos, por lo que puede usar acciones atómicas como decrementAndGet y incrementAndGet.

Para lograr la funcionalidad de esperar hasta que cada uno de los subprocesos de celda esté listo puede usar un CountDownLatch como se menciona en un comentario anterior, o incluso objetos concurrentes como CyclicBarriers para detener la ejecución hasta que todos los subprocesos de celda se unan en la barrera. A través de algunos de estos objetos concurrentes, debería ser más fácil controlar múltiples hilos. Usar la sincronización simple también funciona, solo se requiere que usted haga más codificación y pensamiento para asegurarse de que todo funcione bien.

2

El código puede punto muerto porque no está haciendo ninguna garantía de que los hilos celulares serán realmente en el bloque wait() en el momento en que notifyAll ocurre. La siguiente es una secuencia de eventos que podría causar este problema:

  1. La simulación inicia todos los subprocesos y bloquea esperando un valor 0.
  2. Cada hilo en la secuencia de llamadas de decremento, a continuación, counter.notifyAll, y luego pierde su porción de tiempo
  3. El hilo principal ha sido notificado, se despierta, se encuentra el contador está en 0, llama sync.notifyAll, bucles a la parte superior y espera indefinidamente
  4. Cada hilo en secuencia se da un segmento de tiempo, avanza a la espera(), y espera indefinidamente.
0

¡Precioso ejemplo! No es un punto muerto, ya que, por definición, eso solo puede ocurrir cuando un hilo contiene más de un bloqueo simultáneamente, y otro intenta adquirir los mismos bloqueos en un orden diferente.
Sospecho que el problema aquí es causado por espurios reactivaciones que ocurren en los objetos de celda (si ocurriera un despertar espurio en el objeto de simulación, no tendría efecto ya que se llama a wait() en un bucle que causará la espera para ser reingresado).
Una activación espuria en una celda causará una disminución adicional. Esto a su vez hará que la prueba while(counter.getValue() != 0) pase de largo.
Cambia esa condición a while(counter.getValue() >= 0) y los 'deadlocks deberían desaparecer. Por favor, háganos saber si funciona.

+0

No, no lo creo. Creo que de alguna manera extraña una de las notificaciones, no entiendo muy bien cómo. Pero cuando se bloquea, el contador todavía está en 0. –

+0

Esa fue mi primera idea, pero MK tiene razón. No hace el truco. – marooou

+0

Lo intenté, pero no pude conseguir el bloqueo en vivo. –

0

Esto no es un punto muerto. Su hilo principal puede perder una notificación en el contador y se bloqueará en counter.wait() cuando ya esté en 0. Utilice la herramienta jstack JDK para analizar qué están haciendo los hilos en una situación como esta.