Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

To avoid data corruption in multithreaded Java programs, shared data must be protected from concurrent modifications and accesses. This can be done performed at the object level by using synchronized blocks (coarse-grained locking) , as a result locking which locks out other threads from interfering. If synchronization is used judiciously, deadlocks do should not usually crop up occur (See CON08-J. Do not call alien methods or constructors that synchronize on the same object as the calling class).

Wiki Markup
However, according to the Java Language Specification \[[JLS 05|AA. Java References#JLS 05]\], "The Java programming language neither prevents nor requires detection of deadlock conditions." 

When a fine-grained locking approach is employed by using member locks, deadlocks can still arise unless it is ensured that each thread always requests locks in the same numeric order.

Noncompliant Code Example

This noncompliant code example shows a subtle deadlock issue that manifests itself when synchronization is implemented incorrectly. Assume that an attacker has two bank accounts and is capable of requesting two amount deposit() operations in succession. This corresponds to , as implemented by the two threads as shown started in main(). Objects a and b are constructed such that the amount will be transferred from a to b by first depositing the amount from a to b and then zeroing out a (and vice-versa during the call to b.deposit()).

Code Block
bgColor#FFcccc
class BadLocks {
  private int balance;  // Total amount
	 
  private BadLocks(int balance) {
    this.balance = balance;
  }

  private synchronized void withdraw() {
    System.out.println("Withdrawing amount...");
    this.balance = 0; 
  }

  private synchronized void deposit(BadLocks bl) {
    System.out.println("Depositing amount...");
    bl.balance += this.balance;
    bl.withdraw();
  }

  public static void main(String[] args) throws Exception {
    final BadLocks a = new BadLocks(5000);
    final BadLocks b = new BadLocks(6000);
		
    // These two threads correspond to two malicious requests triggered by the attacker
    Thread t1 = new Thread(new Runnable() {
      public void run() {
        a.deposit(b);
      }
    });
 
    Thread t2 = new Thread(new Runnable() {
      public void run() {
        b.deposit(a);
      }
    });
    t1.start();
    t2.start();
  }
}

Objects a and b are constructed such that the amount will be transferred from a to b by first depositing the amount from a to b and then zeroing out a (and vice-versa during the call to b.deposit()).

Both the threads invoke the synchronized deposit method on objects a and b respectively and there is no interleaving of locks. However, the deposit method is designed to request a lock on object a for the first thread and object b for the second. If both threads acquire separate locks in different orders, for instance, if the call to withdraw() in one thread requests an already held lock in the other thread, a deadlock situation arises. If Thread 1 t1 finishes executing before Thread 2, there would not be any issues. Sequences where the threads alternate, such as, T1, T2, T1, T2 can result in a deadlock (In Tx, 'x' denotes Thread number).

...