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.