2013-12-10 3 views
1

Я собрал некоторый Java-код, который демонстрирует тупик в потоке. По своей сути я обычно получаю 2 строки вывода и исключение, иногда до и иногда после выходных строк, которые ожидаются. Исключением, которое я получаю, является исключение NullPointerException в первой строке метода transfer().Java Threading - решение тупика?

Проблема, с которой я сталкиваюсь, - это то, что я хотел бы знать, как решить эту проблему взаимоблокировки. Я был поиск по StackOverflow этой проблемы и нашли эту страницу:

Avoid Deadlock example

Как решения, я пытался, что Уилл Гартунг и что Dreamcash писал, но я все еще получаю исключение при попытке использовать функцию синхронизации или ReentrantLock объект.

Вот код: класс

счета:

public class Account { 

    int id; 
    double balance; 

    public Account(int id, double balance){ 
     this.id = id; 
     this.balance = balance; 
    } 

    public void withdraw(double amount){ 
     balance = balance - amount; 
    } 

    public void deposit(double amount){ 
     balance = balance + amount; 
    } 

    public int getID(){ 
     return id; 
    } 

    public double getBalance(){ 
     return balance; 
    } 

} 

Bank Class (одноэлементно):

public class Bank{ 

    static Bank bank; 

    Account a1; 
    Account a2; 

    private Bank(){} 

    public static Bank getInstance(){ 
     if(bank==null){ 
      bank = new Bank(); 
      bank.setAccountOne(new Account(1, 100)); 
      bank.setAccountTwo(new Account(2, 100)); 
     } 

     return bank; 
    } 


    public void transfer(Account from, Account to, double amount){ 
     from.withdraw(amount); 
     to.deposit(amount); 
    } 


    public Account getAccountOne(){ 
     return a1; 
    } 

    public Account getAccountTwo(){ 
     return a2; 
    } 
    public void setAccountOne(Account acc){ 
     a1 = acc; 
    } 
    public void setAccountTwo(Account acc){ 
     a2 = acc; 
    } 

} 

PersonOne класс:

Класс
public class PersonOne implements Runnable { 

    public void run() { 

     Bank bank = Bank.getInstance();  

     Account a1 = bank.getAccountOne(); 
     Account a2 = bank.getAccountTwo(); 

     bank.transfer(a2, a1, 10);  

     System.out.println("T1: New balance of A1 is " + a1.getBalance()); 
     System.out.println("T1: New balance of A2 is " + a2.getBalance()); 
    } 

} 

PersonTwo:

public class PersonTwo implements Runnable { 

    public void run() { 
     Bank bank = Bank.getInstance(); 
     Account a1 = bank.getAccountOne(); 
     Account a2 = bank.getAccountTwo(); 

     bank.transfer(a1, a2, 10); 

     System.out.println("T2: New balance of A1 is " + a1.getBalance()); 
     System.out.println("T2: New balance of A2 is " + a2.getBalance()); 
    } 

} 

И, наконец, мой основной метод

public static void main(String[] args){ 
     PersonOne p1 = new PersonOne(); 
     PersonTwo p2 = new PersonTwo(); 

     Thread t1 = new Thread(p1); 
     Thread t2 = new Thread(p2); 

     t1.start(); 
     t2.start(); 
    } 
+0

Где ваши реализации с использованием Lock? Также вы можете посмотреть на атомарные типы данных. –

+0

какие исключения ??? – UmNyobe

+0

Где синхронизированные блоки? –

ответ

8

Исключение я получаю это NullPointerException на первой линии передачи() метод.

Проблема, с которой я сталкиваюсь, - это то, что я хотел бы знать, как решить эту проблему взаимоблокировки.

Ваш код не может спровоцировать какие-либо взаимоблокировки. То, что он вызывает, - написать видимость вопросов: один из потоков получает вызов ленивого инициализатора Bank, а другой поток не видит записи.

Чтобы получить блокировки, вам сначала понадобится блокировка (ключевое слово synchronized). Ваша конкретная проблема NPE будет решена путем добавления synchronized к методу getInstance, и он не будет вводить никаких взаимоблокировок.

Я пришел к выводу, что ваш лучший референт читает некоторые вводные материалы по параллелизму в Java.

+0

+1 Хорошо, что код не близок к тому, чтобы быть потокобезопасным, что является причиной исключения. –

2

Существует целый ряд решений, и некоторые из менее очевидных являются

  • использовать один поток и не использовать замки. В этом примере код будет намного проще и значительно быстрее с одним потоком, поскольку накладные расходы на блокировки превышают выполняемую работу.
  • как это просто пример, вы используете только один глобальный замок. Это не будет работать так же, как и несколько блокировок, но это намного проще, и если вам не нужна производительность, вы должны сделать более простой, с меньшей вероятностью получить ошибки. Это не может зайти в тупик.
  • Если вам нужно использовать несколько блокировок, потому что это домашнее задание, вы можете обеспечить постоянную блокировку в том же порядке. Вы можете сделать это, отсортировав объекты на уникальном ключе и всегда сначала заблокируйте «первый» элемент.
  • Наконец, вы можете заблокировать объекты в любом порядке, используя tryLock во второй учетной записи. Если это не удается, отпустите оба замка и повторите попытку. Вы можете выполнить tryLock на мониторе с помощью Unsafe.tryMonitor().
0

Спасибо за ваши ответы.

В настоящее время я изучаю резьбу. В приведенном выше примере я целенаправленно пытался создать тупик, чтобы я мог научиться решать проблему тупиков (если бы я действительно хотел программу, которая сделала это, я бы просто избегал многопоточности).

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


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

public void transfer(Account from, Account to, double amount){  
    synchronized(from){ 
     synchronized(to){ 
      from.withdraw(amount); 
      to.withdraw(amount); 
     } 
    } 
} 

Но я получаю NullPointerException на синхронном (с) линии.

Я также пробовал это в методе передачи (и учетная запись имела внутри него объект ReentrantLock). Но на этот раз я получаю исключение NullPointerException на строке, которая читает from.getLock(). Unlock()

public void transfer(Account from, Account to, double amount){  
    while(true){ 
     try{ 
      if(from.getLock().tryLock()){ 
       try{ 
        if(to.getLock().tryLock()){ 
         from.withdraw(amount); 
         to.withdraw(amount); 
         break; 
        } 
       } finally { 
        to.getLock().unlock(); 
       }    
      }   
     } finally{ 
      from.getLock().unlock(); 
     } 

     Random random = new Random(); 
     int i = random.nextInt(1000); 
     try { 
      Thread.sleep(1000 + i); 
     } catch (InterruptedException e) { 
      e.printStackTrace(); 
     } 

    } 
} 
+0

Хорошо ... Я просто попытался добавить синхронизацию, чтобы получить метод getInstance(), который предложил Марко, и это решило проблему. Думаю, теперь, когда у меня не было тупика, у меня была проблема видимости. – Chris