Deadlock
A deadlock is when two (or more) threads are stuck forever,
each waiting for the other to release a resource.
The program doesn't crash, it just hangs.
Real-world analogy:
Thread A locks Account 1
Thread B locks Account 2
Now:
A wants Account 2 -> waits for B
B wants Account 1 -> waits for A
public class DeadlockExample {
public static final Object account1 = new Object();
public static final Object account2 = new Object();
public static void main(String[] args) {
Runnable t1 = () -> {
synchronized (account1) {
System.out.println("Thread 1: locked account1");
sleep(100);
System.out.println("Thread 1: waiting for account2");
synchronized(account2) {
System.out.println("Thread 1: locked account2");
}
}
};
Runnable t2 = () -> {
synchronized (account2) {
System.out.println("Thread 2: locked account1");
sleep(100);
System.out.println("Thread 2: waiting for account2");
synchronized(account1) {
System.out.println("Thread 2: locked account2");
}
}
};
Thread thread1 = new Thread(t1, "T1");
Thread thread2 = new Thread(t2, "T2");
thread1.start();
thread2.start();
}
private static void sleep(int miliseconds) {
try {
Thread.sleep(miliseconds);
} catch (InterruptedException e) {}
}
}
FX system (example)
To avoid a deadloc, always lock in the same order.
Avoid nested locks when posible.
Here is a bank transfer example that avoids deadlock by
always locking accounts in the same order.
public class FxSystem {
public static void main(String[] args) {
Account accountA = new Account(1, 1000);
Account accountB = new Account(2, 1000);
BankService bankService = new BankService();
Thread t1 = new Thread(() -> {
bankService.transfer(accountA, accountB, 100);
});
Thread t2 = new Thread(() -> {
bankService.transfer(accountB, accountA, 200);
});
t1.start();
t2.start();
try {
t1.join();
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Account A balance: " + accountA.getBalance());
System.out.println("Account B balance: " + accountB.getBalance());
}
}
class BankService {
public void transfer(Account from, Account to, int amount) {
Account firstLock;
Account secondLock;
if (from.getId() < to.getId()) {
firstLock = from;
secondLock = to;
} else {
firstLock = to;
secondLock = from;
}
synchronized (firstLock) {
synchronized (secondLock) {
if (from.getBalance() < amount) {
System.out.println(
"Not enough money in account " + from.getId());
return;
}
from.withdraw(amount);
to.deposit(amount);
System.out.printf(
"Transferred %d from account %d to account %d %n",
amount, from.getId(), to.getId()
);
}
}
}
}
class Account {
private final int id;
private int balance;
public Account(int id, int balance) {
this.id = id;
this.balance = balance;
}
public int getId() {
return id;
}
public int getBalance() {
return balance;
}
public void withdraw(int amount) {
balance -= amount;
}
public void deposit(int amount) {
balance += amount;
}
}