2011-10-12 15 views
12

tengo el siguiente código Java:punto muerto en código Java con semáforo y adquirir (int)

import java.util.concurrent.*; 

class Foo{ 
    static Semaphore s = new Semaphore(1); 

    public void fun(final char c, final int r){ 
     new Thread(new Runnable(){ 
      public void run(){ 
       try{ 
        s.acquire(r); 
        System.out.println(c+"_"+r); 
        s.release(r+1); 
       } catch(Exception e){ e.printStackTrace(); } 
      } 
     }).start(); 
    } 
} 

class ths{ 
    public static void main(String[]args) throws Exception{ 
     Foo f = new Foo(); 
     f.fun('B',2); 
     f.fun('F',6); 
     f.fun('A',1); 
     f.fun('C',3); 
     f.fun('D',4); 
     f.fun('E',5); 
    } 
} 

Idealmente, esto debería imprimir A_1 través F_6 en orden y salida, pero por alguna razón que no sucede . Por lo general, imprime A_1 y B_2 y luego se atasca.

No encuentro nada obviamente incorrecto con mi código. ¿Alguna sugerencia?

Respuesta

7

El problema básico es que acquire(int permits) no garantiza que todos los permisos sean tomados de una sola vez. Podría adquirir menos permisos y luego bloquear mientras espera el resto.

Consideremos su código. Cuando, por ejemplo, hay tres permisos disponibles, no hay nada que garantice que se otorgarán al hilo C. De hecho, podrían darse al subproceso D para satisfacer parcialmente su solicitud acquire(4), lo que provocaría un interbloqueo.

Si cambia el código como tal, esto soluciona el problema para mí:

public void fun(final char c, final int r){ 
    new Thread(new Runnable(){ 
     public void run(){ 
      try{ 
       while (!s.tryAcquire(r, 1, TimeUnit.MILLISECONDS)) {}; 
       System.out.println(c+"_"+r); 
       s.release(r+1); 
      } catch(Exception e){ e.printStackTrace(); } 
     } 
    }).start(); 
} 

(Pensándolo bien, lo anterior también es roto ya que no hay garantía de la rosca correcta obtener los permisos - podría seguir intentándolo y agotarse el tiempo indefinidamente.)

+0

Eso lo arregla, pero me pregunto por qué es necesario. Si se bloquea después de imprimir B_2, y en el hilo principal, después de un tiempo, imprimo el número de permisos disponibles, imprime 3. Entonces, ¿por qué no se permite que el hilo C continúe? – Vlad

+0

@Vlad: Supongo que el '3' no refleja el hecho de que' D' podría haber "reservado" algunos permisos para su llamada 'acquire (4)'. – NPE

+0

Creo que tienes razón. No había leído la documentación con suficiente cuidado. [enlace] (http://download.oracle.com/javase/6/docs/api/java/util/concurrent/Semaphore.html#release (int)). Había asumido que la implementación pasaría por los hilos y trataría de encontrar uno que realmente pudiera despertar. – Vlad

0

Semaphore adquirir todos los permisos a la vez, de lo contrario no sería un verdadero semaphore. PERO: la versión de Java también tiene una cola de espera interna. Y el comportamiento de esa cola es NOsirve el mejor ajuste de recursos actualmente libres pero más o menos reúne permisos hasta que se pueda permitir la solicitud del primero en la cola. Pero antes de que un hilo ingrese esa cola se realiza una comprobación si los permisos disponibles permiten que el hilo evite entrar en la cola.

he modificado el código para mostrar que el comportamiento de colas:

import java.util.concurrent.*; 
public class SemaphoreTest{ 
    static Semaphore s = new Semaphore(0); 

    public void fun(final char c, final int r) throws Exception { 
     new Thread(new Runnable(){ 
      public void run(){ 
       try{ 
        System.out.println("acquire "+r); 
        s.acquire(r); 
        System.out.println(c+"_"+r); 
       } catch(Exception e){ e.printStackTrace(); } 
      } 
     }).start(); 
     Thread.sleep(500); 
    } 

    public static void main(String[]args) throws Exception{ 
     SemaphoreTest f = new SemaphoreTest(); 

     f.fun('B',2); 
     f.fun('F',6); 
     f.fun('A',1); 
     f.fun('C',3); 
     f.fun('D',4); 
     f.fun('E',5); 

     while(s.hasQueuedThreads()){ 
      Thread.sleep(1000); 
      System.out.println("release "+1+", available "+(s.availablePermits()+1)); 
      s.release(1); 
     } 
    } 
} 

Básicamente los siguientes cambios se han hecho:

  • de inicio con 0 permisos - dejar entrar a nadie en la cola primero.
  • "Defina" el orden de las colas dando a cada hilo 500ms de tiempo después de Thread.start.
  • Cada hilo llamará al acquire pero no al release.
  • El hilo principal alimentará el semáforo lentamente con un permiso después del otro.

Esto dará a esta salida determinista:

acquire 2 
acquire 6 
acquire 1 
acquire 3 
acquire 4 
acquire 5 
release 1, available 1 
release 1, available 2 
B_2 
release 1, available 1 
release 1, available 2 
release 1, available 3 
release 1, available 4 
release 1, available 5 
release 1, available 6 
F_6 
release 1, available 1 
A_1 
release 1, available 1 
release 1, available 2 
release 1, available 3 
C_3 
release 1, available 1 
release 1, available 2 
release 1, available 3 
release 1, available 4 
D_4 
release 1, available 1 
release 1, available 2 
release 1, available 3 
release 1, available 4 
release 1, available 5 
E_5 
release 1, available 1 

Lo que significa que: Cada hilo es despertado, si

  • que está en la cabeza de la cola.
  • se han acumulado suficientes permisos.
Cuestiones relacionadas