liveBook · Manning

Deadlock

As explained in Advanced Algorithms and Data Structures

A deadlock is a state in which concurrent execution cannot proceed because threads wait indefinitely for resources held by one another or for unreleased locks.

A deadlock is a state in which concurrent execution stops because threads wait indefinitely for shared resources or locks held by one another. The application becomes unresponsive and may ultimately crash. section 7.7.1

Consider a concurrent cache protected by a write lock. A thread must release that lock even when an operation fails; otherwise, waiting threads remain blocked. The reliable pattern is to acquire the lock, perform the operation inside try, and unlock in finally. section 7.7.3

Nested cache operations

Suppose set acquires a write lock and calls evictOneEntry. If evictOneEntry also tries to acquire a non-reentrant write lock, the same thread waits for a lock it already holds, while set waits for evictOneEntry to finish. A reentrant lock lets the same thread acquire the held lock again, allowing eviction to continue and avoiding this particular deadlock. section 7.7.4

Reduce deadlock risk by:

  • Releasing locks in a finally block, including when protected operations fail. section 7.7.3
  • Using synchronization mechanisms carefully and avoiding unnecessary concurrent internal structures that can introduce additional locking interactions. section 7.7.1
  • Considering reentrant locks when synchronized methods can call other synchronized methods on the same resource. section 7.7.4
Listing 7.10 Java concurrent LFU cache set
public boolean set(Key key, Value value) {
  writeLock.lock();                                                        #1
  try {                                                                    #2
    if (this.hashTable.contains(key)) {     
      PriorityQueueNode node = this.hashTable.get(key);
      node.setValue(value);  
      return node.updatePriority(node.getCounter());
    } else  if (this.getSize() >= this.maxSize) {
      this.evictOneEntry();                                                #3
    }
    PriorityQueueNode newNode = this.elements.add(key, value, 1);
    this.hashTable.put(key, newNode);
    return true;
  } finally {                                                              #4
    writeLock.unlock();                                                    #5
  }
}

Inspect the concurrent LFU cache set method to see lock acquisition, protected mutation, eviction, and guaranteed unlocking in executable order. section 7.7.3

How Advanced Algorithms and Data Structures covers it

How deep it goes
Explains lock waiting, failure-safe release, and nested non-reentrant acquisition, but does not develop general deadlock detection.
Examples
Uses concurrent LRU and LFU cache methods, especially set calling evictOneEntry while holding a write lock.
What it assumes
Assumes basic familiarity with concurrent threads, shared resources, locks, and synchronized methods.
What you can do after
Diagnose lock-release and nested-acquisition hazards when designing synchronized cache operations.
How it connects
Connects concurrency with locks, synchronization, race conditions, caches, and reentrant locks.

Also explained in

Compare how 15 books explain deadlock →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Advanced Algorithms and Data Structures. The section links point to the parts of the book it draws on.