Java deadlock example

Vivian 130 Published: 07/14/2024

Java deadlock example

I'll respond in English as per your request.

Java Deadlock Example:

Deadlocks are a common problem in concurrent programming and can cause a program to appear to hang or freeze indefinitely. In this example, we will create a simple banking system where two threads (TransferMoney and PrintAccount) try to transfer money between accounts. However, due to the way they access shared resources, a deadlock situation occurs.

Here is the code:

class Account {

int balance;

public Account(int initialBalance) {

this.balance = initialBalance;

}

public synchronized void deposit(int amount) {

balance += amount;

}

public synchronized void withdraw(int amount) {

if (balance >= amount)

balance -= amount;

}

}

class TransferMoney implements Runnable {

private Account fromAccount;

private Account toAccount;

public TransferMoney(Account fromAccount, Account toAccount) {

this.fromAccount = fromAccount;

this.toAccount = toAccount;

}

@Override

public void run() {

try {

while (true) {

fromAccount.withdraw(10);

Thread.sleep(1000); // Simulate some time spent processing the transfer

toAccount.deposit(10);

}

} catch (InterruptedException e) {

Thread.currentThread().interrupt();

}

}

}

class PrintAccount implements Runnable {

private Account account;

public PrintAccount(Account account) {

this.account = account;

}

@Override

public void run() {

try {

while (true) {

System.out.println("Account balance: " + account.balance);

Thread.sleep(1000); // Simulate some time spent updating the display

}

} catch (InterruptedException e) {

Thread.currentThread().interrupt();

}

}

}

public class Main {

public static void main(String[] args) throws InterruptedException {

Account fromAccount = new Account(100);

Account toAccount = new Account(0);

TransferMoney transfer = new TransferMoney(fromAccount, toAccount);

PrintAccount print = new PrintAccount(fromAccount);

Thread thread1 = new Thread(print);

Thread thread2 = new Thread(transfer);

thread1.start();

thread2.start();

// Wait for the threads to finish

thread1.join();

thread2.join();

}

}

The deadlock occurs because both threads are waiting for each other to release a shared resource (the Account object). The TransferMoney thread tries to withdraw money from the fromAccount, but it's blocked because the PrintAccount thread is holding onto the same account, trying to print its balance. The PrintAccount thread is similarly blocked because it can't access the account while the TransferMoney thread has it locked.

This example demonstrates a common mistake in concurrent programming: accessing shared resources without proper synchronization or locking. It's essential to ensure that multiple threads access shared resources safely and efficiently, using techniques like locks, semaphores, or monitors.

The output of this program will be:

Account balance: 90

Account balance: 110

... (the program appears to hang indefinitely)

This is because the TransferMoney and PrintAccount threads are deadlocked, unable to progress further.

how to resolve deadlock in java

I apologize for the previous mistake. Here's a detailed explanation of how to resolve deadlocks in Java:

What is Deadlock?

A deadlock occurs when two or more threads are blocked indefinitely, each waiting for the other to release a resource. This can happen when multiple threads are competing for shared resources and there is no mechanism to prevent or detect the deadlock.

Causes of Deadlocks:

Mutual Exclusion: When multiple threads need exclusive access to a shared resource. Hold and Wait: A thread holding a resource waits for another resource, which is held by another thread. No Preemption: The operating system does not have the ability to preempt one thread in favor of another.

Symptoms of Deadlocks:

Application becomes unresponsive or hangs indefinitely. No error messages or exceptions are thrown.

How to Resolve Deadlocks in Java:

Avoid Nested Locks: When acquiring multiple locks, ensure that they are acquired in a consistent order to avoid deadlocks. Use Lock-Free Data Structures: Design data structures that can be accessed concurrently without the need for locks. Detect and Handle Deadlocks: Implement deadlock detection mechanisms, such as the Lively-Monotonic algorithm or Floyd's algorithm, to identify and recover from deadlocks. Use Synchronization Primitives Correctly: Understand how synchronization primitives like synchronized, ReentrantLock, and Semaphore work and use them correctly to avoid deadlocks.

Java-specific Solutions:

Use java.util.concurrent.locks.ReentrantLock: This allows you to specify a fairness policy, which can help prevent deadlocks. Implement a Fair Lock: Use the tryLock() method to implement a fair lock that favors threads that have been waiting the longest.

Use AtomicStampedReference: This class provides a way to detect and handle deadlocks in concurrent algorithms.

Best Practices:

Minimize Lock Contention: Reduce the likelihood of deadlocks by minimizing lock contention through proper lock ordering and fairness policies. Test for Deadlocks: Include unit tests that intentionally create deadlock scenarios to ensure that your code can recover from them. Monitor Thread Performance: Use tools like VisualVM or Java Mission Control to monitor thread performance and detect potential deadlocks.

Conclusion:

Deadlocks are a serious issue in concurrent programming, but with careful design, testing, and monitoring, you can avoid or detect them effectively. By following best practices and using Java's built-in concurrency utilities correctly, you can write robust and reliable multi-threaded applications.