2011-08-23 1 views
5

Sto cercando di scrivere molto semplice programma che imitano semplice situazione di stallo, in cui infilare un attende Resource Un bloccati da thread B e filettatura B attende per la risorsa B bloccato da Discussione A.Java per i neofiti - imitazione DeadLock

Qui è il mio codice:

//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; 
    } 

} 

Questo è il mio eseguibile che esegue operazione sulla risorsa di cui sopra:

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(); 
    } 

} 

e, infine, la mia classe principale:

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!!!"); 

    } 
} 

Perché questo codice viene eseguito correttamente e non ho e deadLock?

risposta

10

È il potenziale per deadlock - ma entrambi i blocchi vengono acquisiti così rapidamente insieme che un thread può ottenere entrambi prima che l'altro abbia la possibilità di acquisire il primo.

mettere un altro Thread.sleep(500); chiamata tra le due affermazioni sincronizzati e fa situazione di stallo: entrambi i fili entreranno "loro" blocco esterno, il sonno, poi, quando si svegliano faranno entrambi scoprono che il loro blocco "interna" è già acquisita.

Ciò è dovuto al fatto che le istruzioni sincronizzate sono anti-simmetriche: per un thread, il blocco sincronizzato esterno è quello interno per l'altro thread e viceversa.

+0

Il sonno può introdurre non determinismo. Puoi renderlo deterministico; vedi la mia risposta qui sotto. – Toby

+0

@Toby: Sì, è possibile renderlo deterministico, ma 'sleep' è un buon modo per mostrare come può * verificarsi * il deadlock, come puoi sempre immaginare un programma senza sleep in esecuzione in modo simile a uno che * fa * usa sleeps, semplicemente in virtù del fatto che il thread non è pianificato. In altre parole, un programma che fallisce quando si inserisce il sonno è intrinsecamente imperfetto perché si potrebbe vedere lo stesso "accidentalmente" nella vita reale. Lo stesso non è vero con l'inserimento di notifiche. –

5

È possibile che uno dei thread entri in entrambe le sezionisynchronized, bloccando completamente l'altro thread fino al completamento.

4

È necessario simulare "sfortunati tempi". Prova sonno aggiungendo tra due serrature:

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

La ragione di stallo è quel filo A è attendere Discussione B a rilasciare una risorsa prima di A continuare; lo stesso per il thread B, non continuerà finché il thread A non rilascia alcune risorse. In altre parole, A e B si attendono per sempre l'un l'altro.

Nel frammento di codice, sincronizzato può bloccare altri thread perché mentre solo un thread può eseguire il blocco al momento. thread.sleep() sospende il thread per 500 millisecondi, quindi continua. L'attesa per sempre condizione reciproca non soddisfa, questo perché non è un punto morto.

seguente frammento è un buon esempio per illustrare deadlock

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();     
      } 
     } 
    } 
    } 
} 

// ignorare il modo per accedere variabile private nella classe

Poiché non v'è alcun meccanismo assicurare l'ordine di esecuzione dei due fili, è molto probabile che il thread 2 non riceva la notifica da thread1 poiché si avvia da poco, quindi attende la notifica prima di continuare l'esecuzione. Lo stesso di thread1, non può eseguire l'esecuzione successiva finché non riceve la notifica da thread2. entrambi si aspettano l'un l'altro per sempre, tipico stallo.

2

Dorme come suggerito da Jon sopra può introdurre non determinismo, è possibile renderlo deterministico utilizzando invece un coordinatore come un latch.Per chiarire, però, lo considero un problema di test: come provare una situazione di stallo ogni volta e che potrebbe non essere quello che stai cercando.

Vedere questo code per un esempio e un blog post descrivendolo un po '.