2011-08-23 15 views
5

Estoy intentando escribir programa muy simple que imitar DeadLock simple, donde espera enhebrar una de recursos Un bloqueados por el hilo de rosca B y B espera a que el recurso B bloqueado por Tema A.de Java para los principiantes - imitación DeadLock

Aquí está mi código:

//it will be my Shared resource 
public class Account { 
    private float amount; 

    public void debit(double amount){ 
     this.amount-=amount; 
    } 

    public void credit(double amount){ 
     this.amount+=amount; 
    } 

} 

Ésta es mi ejecutable que realiza la operación en el recurso anterior:

public class BankTransaction implements Runnable { 
    Account fromAccount,toAccount; 
    float ammount; 
    public BankTransaction(Account fromAccount, Account toAccount,float ammount){ 
     this.fromAccount = fromAccount; 
     this.toAccount = toAccount; 
     this.ammount = ammount; 
    } 

    private void transferMoney(){ 
     synchronized(fromAccount){ 
      synchronized(toAccount){ 
       fromAccount.debit(ammount); 
       toAccount.credit(ammount); 
       try { 
        Thread.sleep(500); 
       } catch (InterruptedException e) { 
       e.printStackTrace(); 
       } 
       System.out.println("Current Transaction Completed!!!"); 
      } 
     } 
    } 

    @Override 
    public void run() { 
     transferMoney(); 
    } 

} 

y finalmente mi clase principal:

public static void main(String[] args) { 

    Account a = new Account(); 
    Account b = new Account(); 
    Thread thread1 = new Thread(new BankTransaction(a,b,500)); 

    Thread thread2 = new Thread(new BankTransaction(b,a,500)); 
     thread1.start(); 
     thread2.start(); 
System.out.println("Transactions Completed!!!"); 

    } 
} 

¿Por qué se ejecuta este código se ejecuta con éxito y no tengo y deadLock?

Respuesta

10

Tiene el potencial para el punto muerto, pero ambos bloqueos se adquieren tan rápidamente que un hilo puede obtener ambos antes de que el otro tenga la posibilidad de adquirir el primero.

Dicho de otra Thread.sleep(500); llamada entre los dos estados sincronizados y se hace estancamiento: dos hilos entrarán en "su" bloqueo exterior, dormir, a continuación, cuando se despiertan tanto que van a encontrar que su bloqueo "interno" ya está adquirido.

Esto se debe a que las instrucciones sincronizadas son antisimétricas: para una rosca, la traba sincronizada externa es la interna para la otra rosca y viceversa.

+0

El sueño puede introducir no determinismo. Puedes hacerlo determinista; ver mi respuesta a continuación. – Toby

+0

@Toby: Sí, puedes hacerlo determinista, pero 'dormir' es una buena manera de mostrar cómo se puede * encontrar el punto muerto *, ya que siempre puedes imaginar que un programa sin funciones se ejecuta de manera similar a uno * que * usa duerme, simplemente en virtud de que el hilo no está programado. En otras palabras, un programa que falla cuando se inserta duerme es intrínsecamente defectuoso porque podría verse el mismo "accidentalmente" en la vida real. Lo mismo no es cierto con la inserción de notificaciones. –

5

Es posible que uno de los hilos entre ambossynchronized secciones, bloqueando el otro hilo por completo hasta que esté terminado.

4

Tiene que simular 'timing desafortunado'. Trate de añadir el sueño entre dos cerraduras:

synchronized(fromAccount){ 
    Thread.sleep(2000); 
    synchronized(toAccount){ 
0

La razón del estancamiento es que el subproceso A es esperar a que el subproceso B para liberar algún recurso antes de que A continuar; lo mismo para el hilo B, no continuará hasta que el hilo A libere algún recurso. En otras palabras, A y B esperan para siempre el uno al otro.

En el fragmento de código, sincronizado puede bloquear otros subprocesos durante while porque solo un subproceso puede ejecutar el bloque en este momento. thread.sleep() suspende el hilo durante 500 milisegundos, luego continúa. La espera para siempre condición mutua no satisface, por eso no es un punto muerto.

siguiente fragmento es un buen ejemplo para ilustrar punto muerto

public class threadTest{ 

    public class thread1 implements Runnable{ 
     private Thread _th2; 
     private int _foo; 

     public thread1(Thread th2){}; 
     public void run(){ 
     for(int i = 0; i<100; i++){foo += foo;}; 
     synchronized(this){this.notify()}; 
     synchronized(_th2){ 
      _th2.wait(); 
      _foo += _th2.foo; 
      System.out.print(" final result " + _foo); 
     } 
     } 
    } 

    public class thread2 implements Runnable{ 
     private final thread1 _th1; private int _foo; 
     public thread2(thread1 th1){}; 
     public void Run(){ 
      synchronized(_th1){_th1.wait()}; 
      synchronized(this){ 
      _foo += th1._foo(); 
      this.notify();     
      } 
     } 
    } 
    } 
} 

// simplemente ignorar la forma de acceder variable privada en la clase

Debido a que no hay ningún mecanismo de asegurar el orden de ejecución de dos hilos, es muy posible que el subproceso 2 no reciba la notificación del subproceso 1, ya que se inicia recientemente, por lo que espera la notificación antes de continuar la ejecución. Lo mismo para thread1, no puede hacer la próxima ejecución hasta que reciba una notificación de thread2. ambos esperan el uno al otro para siempre, punto muerto típico.

2

Dormir según lo sugerido por Jon arriba puede introducir no determinismo, puede hacerlo determinista utilizando un coordinador como un pestillo en su lugar.Sin embargo, para aclararlo, lo considero un problema de prueba: cómo demostrar un punto muerto en todo momento y puede que eso no sea lo que estás buscando.

Vea este code para un ejemplo y un blog post describiéndolo un poco.