Java для новичков - имитация DeadLock - PullRequest
5 голосов
/ 24 августа 2011

Я пытаюсь написать очень простую программу, которая будет имитировать простой DeadLock, где поток A ожидает ресурс A, заблокированный потоком B, а поток B ждет ресурс B, заблокированный потоком A.

Вот мой код:

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

}  

Это мой runnable, который выполняет операцию над ресурсом выше:

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

}

и, наконец, мой основной класс:

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

    }
}

Почему этот код выполняется успешно, а у меня нет и deadLock?

Ответы [ 5 ]

10 голосов
/ 24 августа 2011

У него есть потенциал для взаимоблокировки - но обе блокировки приобретаются вместе так быстро, что один поток может получить оба, прежде чем другой сможет получить свой первый.

Поместите еще один вызов Thread.sleep(500); между двумя синхронизированными операторами, и он делает взаимоблокировку: оба потока войдут в "свою" внешнюю блокировку, спят, затем, когда они проснутся, оба обнаружат, что их " Внутренний "замок уже приобретен.

Это связано с тем, что синхронизированные операторы являются антисимметричными: для одного потока внешняя синхронизированная блокировка является внутренней для другого потока, и наоборот.

5 голосов
/ 24 августа 2011

Возможно, что один из потоков войдет в обе synchronized секции, полностью блокируя другой поток до его завершения.

4 голосов
/ 24 августа 2011

Вам нужно смоделировать «неудачное время». Попробуйте добавить сон между двумя блокировками:

synchronized(fromAccount){
    Thread.sleep(2000);
    synchronized(toAccount){
2 голосов
/ 26 августа 2011

Сон, как предложено Джоном выше, может привести к недетерминированности, вы можете сделать его детерминированным, используя вместо этого некоторый координатор, например защелку. Чтобы прояснить это, я думаю об этом как о проблеме тестирования: как каждый раз доказывать тупик, и это может быть не то, что вы ищете.

См. Этот код для примера и сообщение в блоге с небольшим описанием.

0 голосов
/ 24 августа 2011

Причина тупика заключается в том, что поток A ожидает, пока поток B освободит некоторый ресурс, прежде чем A продолжит работу;то же самое для потока B, он не будет продолжаться, пока поток A не освободит некоторый ресурс.Другими словами, A и B ждут друг друга вечно.

Во фрагменте кода, synchronized может блокировать другие потоки на время, потому что только один поток может выполнить блок в данный момент.thread.sleep () приостанавливает поток на 500 миллисекунд, затем продолжает.Взаимное условие ожидания навсегда не удовлетворяет, поэтому оно не является тупиком.

Следующий фрагмент является хорошим примером для иллюстрации тупика

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

// просто игнорируем способ доступа к закрытой переменной вкласс

Поскольку нет механизма, обеспечивающего порядок выполнения двух потоков, вполне возможно, что поток 2 не получит уведомление от потока 1, поскольку он запускается в последнее время, поэтому он ожидает уведомления, прежде чем продолжить выполнение.То же самое для thread1, он не может выполнять следующее выполнение, пока не получит уведомление от thread2.они оба ждут друг друга вечно, типичный тупик.

...