0

I am reading the Java Language Specification, and there is a example like this:

class Test {
    public static void main(String[] args) {
        Test t = new Test();
        synchronized(t) {
            synchronized(t) {
                System.out.println("made it!");
            }
        }
    }
}

you can find it from http://docs.oracle.com/javase/specs/jls/se8/html/jls-14.html#jls-14.19

There is a note that

Note that this program would deadlock if a single thread were not permitted to lock a monitor more than once.

What does the note exactly mean? How to make the deadlock using it?

dimo414
  • 47,227
  • 18
  • 148
  • 244
zhouji
  • 5,056
  • 1
  • 26
  • 26

4 Answers4

3

The example shows that synchronized is a reentrant lock.

The synchronized(t) block allows only a single thread to be inside it, (and any other synchronized(t) blocks in the program).

Since synchronized is reentrant, the thread inside the synchronized(t) block can enter another synchronized(t) block although there's already a thread in a synchronized(t) block - that thread itself.

If the lock wasn't reentrant, the second synchronized(t) would create a deadlock since that thread would have to give up the lock before locking again.

Malt
  • 28,965
  • 9
  • 65
  • 105
2

You can't create a deadlock using this code. The text is explaining why the rules are the way they are. The rule is that a thread cannot lock the same monitor more than once. If the thread could lock the same monitor more than once, this is what would happen:

public static void main(String[] args) {
    Test t = new Test();
    synchronized(t) {
        synchronized(t) {
            System.out.println("made it!");
        }
    }
}

The first synchronized would cause the thread to lock t. When the program gets to the second synchronized, the thread would try to lock t, but since t is already locked (by the thread itself), the thread would sit there forever waiting for itself to unlock t, but the thread will never get around to unlocking t since has to wait for itself to unlock t before it can unlock t.

That's the way things would be if the rules were different. But since the rules are the way they are, there is no deadlock.

Note that nobody would actually write code like this. But it's probably pretty common to do the equivalent with methods. Suppose you have two public methods, and they both need to lock something:

public class C {

    SomeObject monitor;

    public void method1() {
        synchronized(monitor) {
            ...stuff...
        }
    }

    public void method2() {
        synchronized(monitor) {
            ...stuff
            method1();
            ...stuff
        }
    }
}

If the rule about not locking more than once weren't there, this would deadlock when method2 calls method1. Since the rule is present, you don't have to worry about deadlock in this case.

ajb
  • 31,309
  • 3
  • 58
  • 84
1

Synchronized blocks in Java are reentrant. This means, that if a Java thread enters a synchronized block of code, and thereby take the lock on the monitor object the block is synchronized on, the thread can enter other Java code blocks synchronized on the same monitor object.

public class Reentrant{

  public synchronized outer(){
    inner();
  }

  public synchronized inner(){
    //do something
  }
}

both outer() and inner() are declared synchronized, which in Java is equivalent to a synchronized(this) block. If a thread calls outer() there is no problem calling inner() from inside outer(), since both methods (or blocks) are synchronized on the same monitor object ("this"). If a thread already holds the lock on a monitor object, it has access to all blocks synchronized on the same monitor object. This is called reentrance. The thread can reenter any block of code for which it already holds the lock.

But if you have your own custom Lock class then you can create deadlock in nested locking on same monitor if its not reenterant.

public class Lock{

  private boolean isLocked = false;

  public synchronized void lock()
  throws InterruptedException{
    while(isLocked){
      wait();
    }
    isLocked = true;
  }

  public synchronized void unlock(){
    isLocked = false;
    notify();
  }
}

public class Reentrant2{

  Lock lock = new Lock();

  public outer(){
    lock.lock();
    inner();
    lock.unlock();
  }

  public synchronized inner(){
    lock.lock();
    //do something
    lock.unlock();
  }
}

A thread calling outer() will first lock the Lock instance. Then it will call inner(). Inside the inner() method the thread will again try to lock the Lock instance. This will fail (meaning the thread will be blocked), since the Lock instance was locked already in the outer() method.

To make the Lock class reentrant we need to make a small change:

public class Lock{

  boolean isLocked = false;
  Thread  lockedBy = null;
  int     lockedCount = 0;

  public synchronized void lock()
  throws InterruptedException{
    Thread callingThread = Thread.currentThread();
    while(isLocked && lockedBy != callingThread){
      wait();
    }
    isLocked = true;
    lockedCount++;
    lockedBy = callingThread;
  }


  public synchronized void unlock(){
    if(Thread.curentThread() == this.lockedBy){
      lockedCount--;

      if(lockedCount == 0){
        isLocked = false;
        notify();
      }
    }
  }

  ...
}

Notice how the while loop (spin lock) now also takes the thread that locked the Lock instance into consideration. If either the lock is unlocked (isLocked = false) or the calling thread is the thread that locked the Lock instance, the while loop will not execute, and the thread calling lock() will be allowed to exit the method.

Additionally, we need to count the number of times the lock has been locked by the same thread. Otherwise, a single call to unlock() will unlock the lock, even if the lock has been locked multiple times. We don't want the lock to be unloced until the thread that locked it, has executed the same amount of unlock() calls as lock() calls.

The Lock class is now ReentrantLock.

In java.util.concurrent package you have ReentrantLock which can be used for reentrance.

check this link to read more about this: http://tutorials.jenkov.com/java-concurrency/locks.html

0

You can't dead lock it like that because you have only one thread.

class DeadLock {
   public synchronized void foo(int i) {
    System.out.println("Processing :" + i );
    if(i == 0) while(true){
       try {
        Thread.sleep(1000);                 //1000 milliseconds is one second.
       } catch(InterruptedException ex) {
         Thread.currentThread().interrupt();
       }
    };
    System.out.println("Finished:" + i );
   }
}

in your main

DeadLock dl = new DeadLock();

Integer[] intArray = {0, 1, 2, 3, 4, 5, 6, 7, 8 };
List<Integer> listOfIntegers =
   new ArrayList<>(Arrays.asList(intArray));
listOfIntegers.stream().parallelStream().forEach(i -> dl.foo(i));

I didnt tested that but when foo(0) will start, it should prevent others to start.

Clem
  • 2,150
  • 15
  • 17