Overview
Deadlock occurs when two or more threads are blocked forever, each waiting for a resource held by the other. Prevention strategies include resource ordering, timeouts, and careful lock acquisition.
1. Understanding Deadlock
Deadlock Conditions & Example
public class DeadlockExample {
static class Resource {
private final String name;
public Resource(String name) {
this.name = name;
}
public String getName() {
return name;
}
public synchronized void use() {
System.out.println(Thread.currentThread().getName() + " using " + name);
try {
Thread.sleep(100); // Simulate work
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
public synchronized void useWith(Resource other) {
System.out.println(Thread.currentThread().getName() +
" attempting to use " + name + " with " + other.getName());
// First lock acquired on 'this'
use();
// Attempt to acquire second lock - POTENTIAL DEADLOCK
other.use();
}
}
public static void main(String[] args) throws InterruptedException {
System.out.println("=== Deadlock Demonstration ===");
Resource resourceA = new Resource("Resource-A");
Resource resourceB = new Resource("Resource-B");
// Thread 1: Lock A then B
Thread t1 = new Thread(() -> {
resourceA.useWith(resourceB);
}, "Thread-1-A-then-B");
// Thread 2: Lock B then A (OPPOSITE ORDER - DEADLOCK!)
Thread t2 = new Thread(() -> {
resourceB.useWith(resourceA);
}, "Thread-2-B-then-A");
t1.start();
t2.start();
// Wait for threads with timeout
t1.join(2000);
t2.join(2000);
if (t1.isAlive() || t2.isAlive()) {
System.out.println("\n*** DEADLOCK DETECTED! ***");
System.out.println("Thread-1 state: " + t1.getState());
System.out.println("Thread-2 state: " + t2.getState());
// Interrupt threads to break deadlock
t1.interrupt();
t2.interrupt();
} else {
System.out.println("No deadlock occurred");
}
t1.join();
t2.join();
}
}
Deadlock Detection Utility
public class DeadlockDetector {
public static void monitorDeadlocks() {
Thread detectorThread = new Thread(() -> {
while (true) {
checkForDeadlocks();
try {
Thread.sleep(5000); // Check every 5 seconds
} catch (InterruptedException e) {
break;
}
}
});
detectorThread.setDaemon(true);
detectorThread.start();
}
private static void checkForDeadlocks() {
ThreadMXBean threadBean = ManagementFactory.getThreadMXBean();
long[] threadIds = threadBean.findDeadlockedThreads();
if (threadIds != null) {
System.out.println("\n*** DEADLOCK DETECTED ***");
ThreadInfo[] threadInfos = threadBean.getThreadInfo(threadIds);
for (ThreadInfo threadInfo : threadInfos) {
System.out.println("Deadlocked Thread: " + threadInfo.getThreadName());
System.out.println(" State: " + threadInfo.getThreadState());
LockInfo lockInfo = threadInfo.getLockInfo();
if (lockInfo != null) {
System.out.println(" Waiting on: " + lockInfo);
}
// Print stack trace
System.out.println(" Stack Trace:");
for (StackTraceElement element : threadInfo.getStackTrace()) {
System.out.println(" " + element);
}
System.out.println();
}
}
}
public static void main(String[] args) throws InterruptedException {
// Start deadlock monitoring
monitorDeadlocks();
// Create deadlock scenario
Object lock1 = new Object();
Object lock2 = new Object();
Thread t1 = new Thread(() -> {
synchronized (lock1) {
System.out.println("Thread-1 acquired lock1");
try { Thread.sleep(100); } catch (InterruptedException e) {}
synchronized (lock2) {
System.out.println("Thread-1 acquired lock2");
}
}
}, "Deadlock-Thread-1");
Thread t2 = new Thread(() -> {
synchronized (lock2) {
System.out.println("Thread-2 acquired lock2");
try { Thread.sleep(100); } catch (InterruptedException e) {}
synchronized (lock1) {
System.out.println("Thread-2 acquired lock1");
}
}
}, "Deadlock-Thread-2");
t1.start();
t2.start();
// Let deadlock occur and be detected
Thread.sleep(3000);
}
}
2. Resource Ordering Prevention
Fixed Order Locking
public class ResourceOrdering {
static class OrderedResource {
private final String name;
private final int orderId;
public OrderedResource(String name, int orderId) {
this.name = name;
this.orderId = orderId;
}
public String getName() { return name; }
public int getOrderId() { return orderId; }
public synchronized void use() {
System.out.println(Thread.currentThread().getName() + " using " + name);
try {
Thread.sleep(50);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
static class ResourceManager {
// Always acquire locks in order of orderId
public static void useResources(OrderedResource res1, OrderedResource res2) {
// Determine lock order based on orderId
OrderedResource first, second;
if (res1.getOrderId() < res2.getOrderId()) {
first = res1;
second = res2;
} else {
first = res2;
second = res1;
}
// Always lock in consistent order
synchronized (first) {
System.out.println(Thread.currentThread().getName() +
" locked " + first.getName());
synchronized (second) {
System.out.println(Thread.currentThread().getName() +
" locked " + second.getName());
// Use both resources
res1.use();
res2.use();
System.out.println(Thread.currentThread().getName() +
" completed work with both resources");
}
}
}
// Alternative: Use System.identityHashCode for ordering when no natural order exists
public static void useResourcesUniversal(Object res1, Object res2) {
Object first, second;
if (System.identityHashCode(res1) < System.identityHashCode(res2)) {
first = res1;
second = res2;
} else {
first = res2;
second = res1;
}
synchronized (first) {
synchronized (second) {
// Critical section
System.out.println(Thread.currentThread().getName() +
" safely acquired both locks");
}
}
}
}
public static void main(String[] args) throws InterruptedException {
System.out.println("=== Resource Ordering Prevention ===");
// Create resources with explicit ordering
OrderedResource resourceA = new OrderedResource("Database", 1);
OrderedResource resourceB = new OrderedResource("FileSystem", 2);
OrderedResource resourceC = new OrderedResource("Network", 3);
// Multiple threads using resources in different combinations
Thread[] threads = new Thread[6];
// Thread 1: A then B
threads[0] = new Thread(() -> {
ResourceManager.useResources(resourceA, resourceB);
}, "Thread-A-B");
// Thread 2: B then A (will be reordered to A then B)
threads[1] = new Thread(() -> {
ResourceManager.useResources(resourceB, resourceA);
}, "Thread-B-A");
// Thread 3: B then C
threads[2] = new Thread(() -> {
ResourceManager.useResources(resourceB, resourceC);
}, "Thread-B-C");
// Thread 4: C then A
threads[3] = new Thread(() -> {
ResourceManager.useResources(resourceC, resourceA);
}, "Thread-C-A");
// Thread 5: All three resources
threads[4] = new Thread(() -> {
// For multiple resources, sort them first
OrderedResource[] resources = {resourceC, resourceA, resourceB};
java.util.Arrays.sort(resources,
(r1, r2) -> Integer.compare(r1.getOrderId(), r2.getOrderId()));
synchronized (resources[0]) {
synchronized (resources[1]) {
synchronized (resources[2]) {
System.out.println(Thread.currentThread().getName() +
" acquired all three resources safely");
}
}
}
}, "Thread-All-Three");
// Thread 6: Universal ordering
threads[5] = new Thread(() -> {
ResourceManager.useResourcesUniversal(new Object(), new Object());
}, "Thread-Universal");
// Start all threads
for (Thread thread : threads) {
thread.start();
}
// Wait for completion
for (Thread thread : threads) {
thread.join();
}
System.out.println("All threads completed without deadlock!");
}
}
Bank Account Transfer with Ordered Locking
public class BankTransferOrdering {
static class BankAccount {
private final String accountNumber;
private double balance;
public BankAccount(String accountNumber, double initialBalance) {
this.accountNumber = accountNumber;
this.balance = initialBalance;
}
public String getAccountNumber() { return accountNumber; }
public double getBalance() { return balance; }
public void debit(double amount) {
if (amount > 0 && balance >= amount) {
balance -= amount;
}
}
public void credit(double amount) {
if (amount > 0) {
balance += amount;
}
}
}
static class Bank {
// Deadlock-prone transfer method
public static void transferWithDeadlock(BankAccount from, BankAccount to, double amount) {
synchronized (from) {
System.out.println(Thread.currentThread().getName() +
" locked " + from.getAccountNumber());
try { Thread.sleep(50); } catch (InterruptedException e) {}
synchronized (to) {
System.out.println(Thread.currentThread().getName() +
" locked " + to.getAccountNumber());
if (from.getBalance() >= amount) {
from.debit(amount);
to.credit(amount);
System.out.println("Transferred " + amount + " from " +
from.getAccountNumber() + " to " + to.getAccountNumber());
}
}
}
}
// Deadlock-free transfer with ordering
public static void transferSafe(BankAccount from, BankAccount to, double amount) {
// Determine lock order based on account number
BankAccount firstLock, secondLock;
int compare = from.getAccountNumber().compareTo(to.getAccountNumber());
if (compare < 0) {
firstLock = from;
secondLock = to;
} else if (compare > 0) {
firstLock = to;
secondLock = from;
} else {
// Same account - no transfer needed
return;
}
synchronized (firstLock) {
System.out.println(Thread.currentThread().getName() +
" locked " + firstLock.getAccountNumber());
synchronized (secondLock) {
System.out.println(Thread.currentThread().getName() +
" locked " + secondLock.getAccountNumber());
if (from.getBalance() >= amount) {
from.debit(amount);
to.credit(amount);
System.out.println("Transferred " + amount + " from " +
from.getAccountNumber() + " to " + to.getAccountNumber());
} else {
System.out.println("Insufficient funds in " + from.getAccountNumber());
}
}
}
}
}
public static void main(String[] args) throws InterruptedException {
System.out.println("=== Bank Transfer Deadlock Prevention ===");
BankAccount account1 = new BankAccount("ACC-001", 1000);
BankAccount account2 = new BankAccount("ACC-002", 1000);
BankAccount account3 = new BankAccount("ACC-003", 1000);
// Test concurrent transfers
Thread t1 = new Thread(() -> {
for (int i = 0; i < 5; i++) {
Bank.transferSafe(account1, account2, 100);
try { Thread.sleep(10); } catch (InterruptedException e) {}
}
}, "Transfer-1-to-2");
Thread t2 = new Thread(() -> {
for (int i = 0; i < 5; i++) {
Bank.transferSafe(account2, account1, 50); // Reverse direction
try { Thread.sleep(15); } catch (InterruptedException e) {}
}
}, "Transfer-2-to-1");
Thread t3 = new Thread(() -> {
for (int i = 0; i < 5; i++) {
Bank.transferSafe(account1, account3, 75);
try { Thread.sleep(20); } catch (InterruptedException e) {}
}
}, "Transfer-1-to-3");
Thread t4 = new Thread(() -> {
for (int i = 0; i < 5; i++) {
Bank.transferSafe(account3, account2, 25);
try { Thread.sleep(25); } catch (InterruptedException e) {}
}
}, "Transfer-3-to-2");
t1.start();
t2.start();
t3.start();
t4.start();
t1.join();
t2.join();
t3.join();
t4.join();
System.out.println("\nFinal Balances:");
System.out.println("Account 1: " + account1.getBalance());
System.out.println("Account 2: " + account2.getBalance());
System.out.println("Account 3: " + account3.getBalance());
System.out.println("Total: " + (account1.getBalance() + account2.getBalance() + account3.getBalance()));
}
}
3. Timeout-Based Prevention
Try-Lock with Timeout
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class TimeoutPrevention {
static class TimeoutResource {
private final String name;
private final Lock lock = new ReentrantLock();
public TimeoutResource(String name) {
this.name = name;
}
public String getName() { return name; }
public boolean tryUse(long timeout, TimeUnit unit) throws InterruptedException {
if (lock.tryLock(timeout, unit)) {
try {
System.out.println(Thread.currentThread().getName() +
" acquired " + name);
Thread.sleep(100); // Simulate work
return true;
} finally {
lock.unlock();
System.out.println(Thread.currentThread().getName() +
" released " + name);
}
}
return false;
}
public void use() throws InterruptedException {
lock.lock();
try {
System.out.println(Thread.currentThread().getName() +
" using " + name);
Thread.sleep(100);
} finally {
lock.unlock();
}
}
}
static class TimeoutResourceManager {
// Attempt to acquire both resources with timeout
public static boolean useBothWithTimeout(TimeoutResource res1,
TimeoutResource res2,
long timeout,
TimeUnit unit) throws InterruptedException {
long startTime = System.nanoTime();
long timeoutNanos = unit.toNanos(timeout);
// Try to acquire first lock
if (!res1.tryUse(timeout, unit)) {
System.out.println(Thread.currentThread().getName() +
" failed to acquire " + res1.getName());
return false;
}
try {
// Calculate remaining time for second lock
long elapsed = System.nanoTime() - startTime;
long remainingTime = timeoutNanos - elapsed;
if (remainingTime <= 0) {
System.out.println(Thread.currentThread().getName() +
" timeout acquiring " + res2.getName());
return false;
}
// Try to acquire second lock with remaining time
if (res2.tryUse(remainingTime, TimeUnit.NANOSECONDS)) {
System.out.println(Thread.currentThread().getName() +
" successfully used both resources");
return true;
} else {
System.out.println(Thread.currentThread().getName() +
" failed to acquire " + res2.getName());
return false;
}
} finally {
// First lock will be released automatically by tryUse
}
}
// Alternative: Acquire in ordered fashion with timeout
public static boolean useBothOrderedWithTimeout(TimeoutResource res1,
TimeoutResource res2,
long timeout,
TimeUnit unit) throws InterruptedException {
// Determine order
TimeoutResource first, second;
if (res1.getName().compareTo(res2.getName()) < 0) {
first = res1;
second = res2;
} else {
first = res2;
second = res1;
}
return useBothWithTimeout(first, second, timeout, unit);
}
}
public static void main(String[] args) throws InterruptedException {
System.out.println("=== Timeout-Based Deadlock Prevention ===");
TimeoutResource resourceA = new TimeoutResource("Resource-A");
TimeoutResource resourceB = new TimeoutResource("Resource-B");
// Thread with sufficient timeout
Thread t1 = new Thread(() -> {
try {
boolean success = TimeoutResourceManager.useBothWithTimeout(
resourceA, resourceB, 2, TimeUnit.SECONDS);
System.out.println("Thread-1 result: " + (success ? "SUCCESS" : "FAILED"));
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}, "Thread-1-Long-Timeout");
// Thread with very short timeout (will likely fail)
Thread t2 = new Thread(() -> {
try {
boolean success = TimeoutResourceManager.useBothOrderedWithTimeout(
resourceB, resourceA, 50, TimeUnit.MILLISECONDS);
System.out.println("Thread-2 result: " + (success ? "SUCCESS" : "FAILED"));
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}, "Thread-2-Short-Timeout");
// Thread that causes contention
Thread t3 = new Thread(() -> {
try {
// Hold resource A for a while
if (resourceA.tryUse(1, TimeUnit.SECONDS)) {
System.out.println("Thread-3 holding Resource-A");
Thread.sleep(500); // Hold the lock
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}, "Thread-3-Contention");
t3.start();
Thread.sleep(100); // Let t3 acquire the lock first
t1.start();
t2.start();
t1.join();
t2.join();
t3.join();
System.out.println("Timeout prevention demo completed");
}
}
4. Lock Hierarchy & Resource Management
Hierarchical Locking System
public class HierarchicalLocking {
static class LockLevel {
public static final int DATABASE = 1;
public static final int FILE_SYSTEM = 2;
public static final int NETWORK = 3;
public static final int MEMORY = 4;
}
static class HierarchicalResource {
private final String name;
private final int level;
private final Object lock = new Object();
private boolean locked = false;
public HierarchicalResource(String name, int level) {
this.name = name;
this.level = level;
}
public String getName() { return name; }
public int getLevel() { return level; }
public boolean acquire() {
synchronized (lock) {
if (!locked) {
locked = true;
System.out.println(Thread.currentThread().getName() +
" acquired " + name + " (level " + level + ")");
return true;
}
return false;
}
}
public void release() {
synchronized (lock) {
locked = false;
System.out.println(Thread.currentThread().getName() +
" released " + name);
lock.notifyAll();
}
}
public boolean waitAndAcquire(long timeout) throws InterruptedException {
synchronized (lock) {
long endTime = System.currentTimeMillis() + timeout;
long remaining = timeout;
while (locked && remaining > 0) {
lock.wait(remaining);
remaining = endTime - System.currentTimeMillis();
}
if (!locked) {
locked = true;
System.out.println(Thread.currentThread().getName() +
" acquired " + name + " after wait");
return true;
}
return false;
}
}
}
static class HierarchicalResourceManager {
// Acquire resources in level order (lowest to highest)
public static boolean acquireResources(HierarchicalResource[] resources,
long timeout) throws InterruptedException {
// Sort resources by level
java.util.Arrays.sort(resources,
(r1, r2) -> Integer.compare(r1.getLevel(), r2.getLevel()));
boolean allAcquired = false;
int acquiredCount = 0;
try {
long startTime = System.currentTimeMillis();
long remaining = timeout;
// Acquire in order
for (HierarchicalResource resource : resources) {
if (remaining <= 0) {
break;
}
if (resource.waitAndAcquire(remaining)) {
acquiredCount++;
} else {
break;
}
// Update remaining time
long elapsed = System.currentTimeMillis() - startTime;
remaining = timeout - elapsed;
}
allAcquired = (acquiredCount == resources.length);
if (allAcquired) {
System.out.println(Thread.currentThread().getName() +
" successfully acquired all resources");
return true;
} else {
System.out.println(Thread.currentThread().getName() +
" failed to acquire all resources");
return false;
}
} finally {
// Release all acquired resources if we didn't get all
if (!allAcquired) {
for (int i = 0; i < acquiredCount; i++) {
resources[i].release();
}
}
}
}
public static void releaseResources(HierarchicalResource[] resources) {
// Release in reverse order (not strictly necessary but good practice)
for (int i = resources.length - 1; i >= 0; i--) {
resources[i].release();
}
}
}
public static void main(String[] args) throws InterruptedException {
System.out.println("=== Hierarchical Locking ===");
// Create resources with different levels
HierarchicalResource database = new HierarchicalResource("Database", LockLevel.DATABASE);
HierarchicalResource filesystem = new HierarchicalResource("FileSystem", LockLevel.FILE_SYSTEM);
HierarchicalResource network = new HierarchicalResource("Network", LockLevel.NETWORK);
HierarchicalResource memory = new HierarchicalResource("Memory", LockLevel.MEMORY);
HierarchicalResource[] allResources = {database, filesystem, network, memory};
// Thread 1: Acquire all resources in correct order
Thread t1 = new Thread(() -> {
try {
if (HierarchicalResourceManager.acquireResources(allResources, 3000)) {
try {
System.out.println("Thread-1 working with all resources...");
Thread.sleep(1000);
} finally {
HierarchicalResourceManager.releaseResources(allResources);
}
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}, "Thread-1-All-Resources");
// Thread 2: Try to acquire subset (should work due to hierarchy)
Thread t2 = new Thread(() -> {
try {
HierarchicalResource[] subset = {database, filesystem};
if (HierarchicalResourceManager.acquireResources(subset, 2000)) {
try {
System.out.println("Thread-2 working with database and filesystem...");
Thread.sleep(500);
} finally {
HierarchicalResourceManager.releaseResources(subset);
}
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}, "Thread-2-Subset");
// Thread 3: Try to acquire in different order (will be reordered by manager)
Thread t3 = new Thread(() -> {
try {
HierarchicalResource[] reverseOrder = {memory, network, database};
if (HierarchicalResourceManager.acquireResources(reverseOrder, 2000)) {
try {
System.out.println("Thread-3 working with reordered resources...");
Thread.sleep(300);
} finally {
HierarchicalResourceManager.releaseResources(reverseOrder);
}
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}, "Thread-3-Reverse-Order");
t1.start();
Thread.sleep(100); // Let t1 start first
t2.start();
t3.start();
t1.join();
t2.join();
t3.join();
System.out.println("Hierarchical locking demo completed");
}
}
5. Deadlock Prevention with Resource Pool
Resource Pool with Timeout
import java.util.*;
import java.util.concurrent.*;
public class ResourcePoolPrevention {
static class PooledResource {
private final String name;
private volatile boolean inUse = false;
private final Object lock = new Object();
public PooledResource(String name) {
this.name = name;
}
public String getName() { return name; }
public boolean acquire(long timeout, TimeUnit unit) throws InterruptedException {
synchronized (lock) {
long endTime = System.currentTimeMillis() + unit.toMillis(timeout);
long remaining = unit.toMillis(timeout);
while (inUse && remaining > 0) {
lock.wait(remaining);
remaining = endTime - System.currentTimeMillis();
}
if (!inUse) {
inUse = true;
System.out.println(Thread.currentThread().getName() +
" acquired " + name);
return true;
}
return false;
}
}
public void release() {
synchronized (lock) {
inUse = false;
System.out.println(Thread.currentThread().getName() +
" released " + name);
lock.notifyAll();
}
}
public void use() throws InterruptedException {
System.out.println(Thread.currentThread().getName() +
" using " + name);
Thread.sleep(100 + new Random().nextInt(100)); // Simulate work
}
}
static class ResourcePool {
private final Map<String, PooledResource> resources;
public ResourcePool(Collection<PooledResource> resources) {
this.resources = new HashMap<>();
for (PooledResource resource : resources) {
this.resources.put(resource.getName(), resource);
}
}
// Try to acquire multiple resources with deadlock prevention
public List<PooledResource> acquireResources(Set<String> resourceNames,
long timeout,
TimeUnit unit) throws InterruptedException {
// Convert to list and sort to ensure consistent order
List<String> orderedNames = new ArrayList<>(resourceNames);
Collections.sort(orderedNames);
List<PooledResource> acquired = new ArrayList<>();
boolean success = false;
try {
long startTime = System.currentTimeMillis();
long remaining = unit.toMillis(timeout);
// Acquire in consistent order
for (String name : orderedNames) {
if (remaining <= 0) {
break;
}
PooledResource resource = resources.get(name);
if (resource != null && resource.acquire(remaining, TimeUnit.MILLISECONDS)) {
acquired.add(resource);
} else {
break;
}
// Update remaining time
long elapsed = System.currentTimeMillis() - startTime;
remaining = unit.toMillis(timeout) - elapsed;
}
success = (acquired.size() == resourceNames.size());
if (success) {
System.out.println(Thread.currentThread().getName() +
" successfully acquired all requested resources");
return acquired;
} else {
System.out.println(Thread.currentThread().getName() +
" failed to acquire all resources");
return Collections.emptyList();
}
} finally {
// Release all if we didn't succeed
if (!success) {
releaseResources(acquired);
}
}
}
public void releaseResources(List<PooledResource> resources) {
// Release in reverse order (good practice)
for (int i = resources.size() - 1; i >= 0; i--) {
resources.get(i).release();
}
}
// Single resource acquisition with fallback
public PooledResource acquireResource(String name, long timeout, TimeUnit unit)
throws InterruptedException {
PooledResource resource = resources.get(name);
if (resource != null && resource.acquire(timeout, unit)) {
return resource;
}
return null;
}
}
public static void main(String[] args) throws InterruptedException {
System.out.println("=== Resource Pool Deadlock Prevention ===");
// Create resource pool
List<PooledResource> resourceList = Arrays.asList(
new PooledResource("Printer"),
new PooledResource("Scanner"),
new PooledResource("Database"),
new PooledResource("Network")
);
ResourcePool pool = new ResourcePool(resourceList);
// Multiple threads requesting different resource combinations
Thread t1 = new Thread(() -> {
try {
Set<String> requested = new HashSet<>(Arrays.asList("Printer", "Scanner"));
List<PooledResource> acquired = pool.acquireResources(requested, 2000, TimeUnit.MILLISECONDS);
if (!acquired.isEmpty()) {
try {
System.out.println("Thread-1 working with acquired resources...");
for (PooledResource resource : acquired) {
resource.use();
}
} finally {
pool.releaseResources(acquired);
}
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}, "Thread-1-Print-Scan");
Thread t2 = new Thread(() -> {
try {
Set<String> requested = new HashSet<>(Arrays.asList("Scanner", "Database"));
List<PooledResource> acquired = pool.acquireResources(requested, 2000, TimeUnit.MILLISECONDS);
if (!acquired.isEmpty()) {
try {
System.out.println("Thread-2 working with acquired resources...");
for (PooledResource resource : acquired) {
resource.use();
}
} finally {
pool.releaseResources(acquired);
}
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}, "Thread-2-Scan-DB");
Thread t3 = new Thread(() -> {
try {
Set<String> requested = new HashSet<>(Arrays.asList("Database", "Network", "Printer"));
List<PooledResource> acquired = pool.acquireResources(requested, 3000, TimeUnit.MILLISECONDS);
if (!acquired.isEmpty()) {
try {
System.out.println("Thread-3 working with acquired resources...");
for (PooledResource resource : acquired) {
resource.use();
}
} finally {
pool.releaseResources(acquired);
}
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}, "Thread-3-All-Three");
t1.start();
t2.start();
t3.start();
t1.join();
t2.join();
t3.join();
System.out.println("Resource pool demo completed successfully");
}
}
6. Best Practices & Patterns
Comprehensive Deadlock Prevention Strategy
public class DeadlockPreventionStrategies {
// Strategy 1: Resource ordering
public static class OrderedLocking {
public static void acquireLocks(Object lock1, Object lock2) {
// Determine order using hash code
Object first, second;
if (System.identityHashCode(lock1) < System.identityHashCode(lock2)) {
first = lock1;
second = lock2;
} else {
first = lock2;
second = lock1;
}
synchronized (first) {
synchronized (second) {
// Critical section
System.out.println(Thread.currentThread().getName() +
" acquired both locks safely");
}
}
}
}
// Strategy 2: Try-lock with timeout
public static class TimeoutLocking {
public static boolean tryAcquireLocks(Object lock1, Object lock2, long timeoutMs)
throws InterruptedException {
long startTime = System.currentTimeMillis();
long remaining = timeoutMs;
// Try to acquire first lock
synchronized (lock1) {
System.out.println(Thread.currentThread().getName() +
" acquired first lock");
// Calculate remaining time
long elapsed = System.currentTimeMillis() - startTime;
remaining = timeoutMs - elapsed;
if (remaining <= 0) {
return false; // Timeout
}
// Try to acquire second lock with remaining time
if (Thread.holdsLock(lock2)) {
// Already have the lock (reentrant)
return true;
}
// Use wait/notify to simulate try-lock with timeout
long waitEnd = System.currentTimeMillis() + remaining;
while (System.currentTimeMillis() < waitEnd) {
try {
lock2.wait(remaining);
synchronized (lock2) {
System.out.println(Thread.currentThread().getName() +
" acquired second lock");
return true;
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw e;
}
}
return false; // Timeout on second lock
}
}
}
// Strategy 3: Lock hierarchy validation
public static class HierarchicalValidator {
private static final ThreadLocal<Set<Object>> acquiredLocks =
ThreadLocal.withInitial(HashSet::new);
public static void validateLockOrder(Object lock) {
Set<Object> currentLocks = acquiredLocks.get();
// Check if this lock violates any hierarchy
for (Object acquiredLock : currentLocks) {
if (System.identityHashCode(lock) < System.identityHashCode(acquiredLock)) {
throw new IllegalStateException(
"Lock order violation: trying to acquire " + lock +
" after " + acquiredLock);
}
}
currentLocks.add(lock);
}
public static void releaseLock(Object lock) {
acquiredLocks.get().remove(lock);
}
public static void acquireWithValidation(Object lock1, Object lock2) {
validateLockOrder(lock1);
validateLockOrder(lock2);
synchronized (lock1) {
synchronized (lock2) {
System.out.println(Thread.currentThread().getName() +
" acquired locks with validation");
}
}
releaseLock(lock2);
releaseLock(lock1);
}
}
// Strategy 4: Resource allocation graph algorithm simulation
public static class DeadlockDetector {
private static final Map<Object, Set<Object>> allocationGraph = new ConcurrentHashMap<>();
private static final Object graphLock = new Object();
public static boolean wouldCauseDeadlock(Object requestedLock, Thread thread) {
synchronized (graphLock) {
// Simplified deadlock detection
// In real implementation, you'd use proper graph cycle detection
Set<Object> currentlyHeld = getLocksHeldByThread(thread);
// Check if any thread holding the requested lock is waiting for locks we hold
for (Object heldLock : currentlyHeld) {
Set<Object> waitFor = allocationGraph.get(heldLock);
if (waitFor != null && waitFor.contains(requestedLock)) {
return true; // Potential deadlock detected
}
}
return false;
}
}
public static void recordAllocation(Object lock, Thread thread) {
synchronized (graphLock) {
allocationGraph.computeIfAbsent(lock, k -> new HashSet<>())
.addAll(getLocksHeldByThread(thread));
}
}
public static void recordRelease(Object lock) {
synchronized (graphLock) {
allocationGraph.remove(lock);
}
}
private static Set<Object> getLocksHeldByThread(Thread thread) {
// This is a simplified version
// In real implementation, you'd track which locks each thread holds
return Collections.emptySet();
}
}
public static void main(String[] args) throws InterruptedException {
System.out.println("=== Comprehensive Deadlock Prevention ===");
Object lockA = new Object();
Object lockB = new Object();
Object lockC = new Object();
// Test Ordered Locking
System.out.println("\n1. Testing Ordered Locking:");
Thread t1 = new Thread(() -> {
OrderedLocking.acquireLocks(lockA, lockB);
}, "Ordered-Thread");
t1.start();
t1.join();
// Test Timeout Locking
System.out.println("\n2. Testing Timeout Locking:");
Thread t2 = new Thread(() -> {
try {
boolean success = TimeoutLocking.tryAcquireLocks(lockB, lockC, 1000);
System.out.println("Timeout locking result: " + success);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}, "Timeout-Thread");
t2.start();
t2.join();
// Test Hierarchical Validation
System.out.println("\n3. Testing Hierarchical Validation:");
Thread t3 = new Thread(() -> {
try {
HierarchicalValidator.acquireWithValidation(lockC, lockA);
} catch (IllegalStateException e) {
System.out.println("Validation caught: " + e.getMessage());
}
}, "Validation-Thread");
t3.start();
t3.join();
System.out.println("\nAll prevention strategies demonstrated successfully");
}
}
Key Prevention Strategies Summary
- Resource Ordering: Always acquire locks in a consistent global order
- Timeout Mechanisms: Use try-lock with timeout to avoid indefinite waiting
- Lock Hierarchy: Define and enforce a locking hierarchy
- Resource Pooling: Use managed resource pools with acquisition policies
- Deadlock Detection: Monitor and detect potential deadlocks at runtime
- Avoid Nested Locks: Design systems to minimize lock nesting when possible
Best Practices
- Always use timeouts when acquiring multiple locks
- Establish and follow a lock ordering convention
- Use higher-level concurrency utilities from
java.util.concurrent - Keep critical sections as small as possible
- Test concurrent code thoroughly with stress tests
- Monitor applications for potential deadlocks in production
Deadlock prevention requires careful design and consistent application of these strategies throughout your codebase.