Reputation: 899
I have put together some Java code which demonstrates deadlock in threading. On it's own, I usually get 2 lines output and an exception, sometimes before and sometimes after the output lines which is expected. The exception I get is a NullPointerException on the first line of the transfer() method.
The problem I'm having is I'd like to know how to solve this deadlock problem. I have had a search on StackOverflow of this problem and found this page:
As solutions, I have tried what Will Hartung and what Dreamcash posted but I still get exceptions when trying to use synchronize or a ReentrantLock object.
Here's the code:
Account class:
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 (a singleton):
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 class:
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 class:
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());
}
}
And finally, my main method
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();
}
Upvotes: 1
Views: 1716
Reputation: 899
Thanks for your answers.
I'm currently learning about threading. In the above, I was purposefully trying to create a deadlock, so that I could learn how to solve the problem of deadlocks (if I actually wanted a program that did the above, I would just avoid multi-threading).
The reason I used a singleton was so the two threads would be trying to use the same objects.
So... the above was the code I used to try to recreate a deadlock, so I was expecting problems/exceptions when running. One way I tried to then fix it was by rewriting the transfer method to look like this:
public void transfer(Account from, Account to, double amount){
synchronized(from){
synchronized(to){
from.withdraw(amount);
to.withdraw(amount);
}
}
}
But I'd get a NullPointerException on the synchronized(from) line.
I also tried this in the transfer method (and account had a ReentrantLock object inside it). But this time I'd get a NullPointerException on the line that reads 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();
}
}
}
Upvotes: 0
Reputation: 533870
There is a number of solutions and some of the less obvious ones are
Upvotes: 2
Reputation: 200296
The exception I get is a NullPointerException on the first line of the transfer() method.
The problem I'm having is I'd like to know how to solve this deadlock problem.
Your code cannot possible provoke any deadlocks. What it does provoke is write visibility issues: one of the threads gets to invoke the lazy Bank
initializer and the other thread does not see the write.
To get deadlocks you first need any kind of locking (the synchronized
keyword). Your particular NPE problem will be solved by adding synchronized
to the getInstance
method and it will not introduce any deadlocks.
My conclusion is that your best recourse is reading some introductory material on concurrency in Java.
Upvotes: 8