87

I recently got this questions asked in an interview.

I answered that deadlock occurs if the interleaving goes wrong, but the interviewer insisted that a program that will always go into deadlock regardless of interleaving can be written .

Can we write such a program ? Can you point me to some example program like that ?

Greg Mattes
  • 33,090
  • 15
  • 73
  • 105
user2434
  • 6,339
  • 18
  • 63
  • 87
  • 3
    The interviewer is ***definitely*** a foolish fellow. – Lion Jan 19 '12 at 18:04
  • 24
    The interviewer is certainly not a foolish fellow. Full understanding of a subject means you should be able to explain polar edge cases: making program to never lock, and to always lock. – Yuriy Zubarev Feb 15 '12 at 01:40

13 Answers13

102

UPDATE: This question was the subject of my blog in January 2013. Thanks for the great question!


How can we write a program that will always go into deadlock no matter how the threads are scheduled?

Here's an example in C#. Note that the program appears to contain no locks and no shared data. It has only a single local variable and three statements, and yet it deadlocks with 100% certainty. One would be hard-pressed to come up with a simpler program that deadlocks with certainty.

Exercise to the reader #1: explain how this deadlocks. (An answer is in the comments.)

Exercise to the reader #2: demonstrate the same deadlock in Java. (An answer is here: https://stackoverflow.com/a/9286697/88656)

class MyClass
{
  static MyClass() 
  {
    // Let's run the initialization on another thread!
    var thread = new System.Threading.Thread(Initialize);
    thread.Start();
    thread.Join();
  }

  static void Initialize() 
  { /* TODO: Add initialization code */ }

  static void Main() 
  { }
}
Community
  • 1
  • 1
Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
  • 4
    My knowledge of theoretical C# is limited, but I assume the classloader guarantees the code is run single threaded as it does in Java. I'm pretty sure there's a similar example in Java Puzzlers. – Voo Jan 16 '12 at 16:36
  • 12
    @Voo: You have a good memory. Neal Gafter -- the co-author of "Java Puzzlers" -- and I presented a rather more obfuscated version of this code in our "C# Puzzlers" talk at the Oslo Developer Conference a few years ago. – Eric Lippert Jan 16 '12 at 16:40
  • @EricLippert - Do you care to share a link with some explanation for those interested (like me) but with lacking knowledge (like me again :) – Lieven Keersmaekers Jan 17 '12 at 08:31
  • 41
    @Lieven: The static constructor must run no more than *once* and it must run *before* the first call to any static method in the class. Main is a static method, so the main thread calls the static ctor. To ensure it only runs once, the CLR takes out a lock that is not released until the static ctor finishes. When the ctor starts a new thread, that thread also calls a static method, so the CLR tries to take the lock to see if it needs to run the ctor. The main thread meanwhile "joins" the blocked thread, and now we have our deadlock. – Eric Lippert Jan 17 '12 at 14:28
  • 34
    @artbristol: I've never written so much as a line of Java code; I see no reason to start now. – Eric Lippert Jan 17 '12 at 19:14
  • 4
    Oh, I assumed you had an answer to your Exercise #2. Congrats on getting this many upvotes for answering a Java question. – artbristol Jan 17 '12 at 21:04
  • 3
    @EricLippert I'm trying to understand exactly what is going on here. When you say the main thread "joins" the blocked thread, I don't see how it is Joining. Isn't the main thread stuck at `thread.Join();` and it never actually joins? The way I am seeing it is that `MyClass` static ctor is called which takes a lock and then the `Initialize` tries to call the ctor again but cannot due to the lock. So the main thread would be forever stuck by `thread.Join();` – Justin Apr 02 '13 at 15:26
  • 1
    @Justin: The question was "how can I write a program that always deadlocks?". Your analysis, which is correct, shows that the main thread never gets through the Join. That's what "deadlocks" means -- that there are two threads both waiting on each other. – Eric Lippert Apr 02 '13 at 15:44
  • One of the warnings of the Join method is to avoid this kind of scenarios (https://learn.microsoft.com/en-us/dotnet/api/system.threading.thread.join) You should never call the Join method of the Thread object that represents the current thread from the current thread. This causes your app to become unresponsive because the current thread waits upon itself indefinitely, – Oscar Acevedo Feb 10 '20 at 19:38
  • 1
    @OscarAcevedo: But that is not what we do here. We start a new thread, and then join the current thread with that new thread. In my example here we never join a thread to itself! – Eric Lippert Feb 10 '20 at 19:40
27

The latch here ensure that both locks are held when each thread tries to lock the other:

import java.util.concurrent.CountDownLatch;

public class Locker extends Thread {

   private final CountDownLatch latch;
   private final Object         obj1;
   private final Object         obj2;

   Locker(Object obj1, Object obj2, CountDownLatch latch) {
      this.obj1 = obj1;
      this.obj2 = obj2;
      this.latch = latch;
   }

   @Override
   public void run() {
      synchronized (obj1) {

         latch.countDown();
         try {
            latch.await();
         } catch (InterruptedException e) {
            throw new RuntimeException();
         }
         synchronized (obj2) {
            System.out.println("Thread finished");
         }
      }

   }

   public static void main(String[] args) {
      final Object obj1 = new Object();
      final Object obj2 = new Object();
      final CountDownLatch latch = new CountDownLatch(2);

      new Locker(obj1, obj2, latch).start();
      new Locker(obj2, obj1, latch).start();

   }

}

Interesting to run jconsole, which will correctly show you the deadlock in the Threads tab.

artbristol
  • 32,010
  • 5
  • 70
  • 103
  • 3
    That's the best so far, but I'd replace `sleep` with a proper latch: theoretically, we have a race condition here. While we can be almost sure 0.5 sec is enough, it's not too good for an interview task. – alf Jan 16 '12 at 13:30
25

Deadlock happens when threads (or whatever your platform calls its execution units) acquire resources, where each resource can only be held by one thread at a time, and holds on to those resources in a such a way that the holds cannot be preempted, and there exists some "circular" relationship between the threads such that each thread in the deadlock is waiting to acquire some resource held by another thread.

So, an easy way to avoid deadlock is to give some total ordering to resources and impose a rule that resources are only ever acquired by threads in order. Conversely, a deadlock can be intentionally created by running threads that acquire resources, but do not acquire them in order. For example:

Two threads, two locks. The first thread runs a loop that attempts to acquire the locks in a certain order, the second thread runs a loop that attempts to acquire the locks in the opposite order. Each thread releases both locks after successfully acquiring the locks.

public class HighlyLikelyDeadlock {
    static class Locker implements Runnable {
        private Object first, second;

        Locker(Object first, Object second) {
            this.first = first;
            this.second = second;
        }

        @Override
        public void run() {
            while (true) {
                synchronized (first) {
                    synchronized (second) {
                        System.out.println(Thread.currentThread().getName());
                    }
                }
            }
        }
    }

    public static void main(final String... args) {
        Object lock1 = new Object(), lock2 = new Object();
        new Thread(new Locker(lock1, lock2), "Thread 1").start();
        new Thread(new Locker(lock2, lock1), "Thread 2").start();
    }
}

Now, there have been a few comments in this question that point out the difference between the likelihood and the certainty of deadlock. In some sense, the distinction is an academic issue. From a practical standpoint, I'd certainly like to see a running system that doesn't deadlock with the code I've written above :)

However, interview questions can be academic at times, and this SO question does have the word "surely" in the title, so what follows is a program that certainly deadlocks. Two Locker objects are created, each is given two locks and a CountDownLatch used to synchronize between the threads. Each Locker locks the first lock then counts down the latch once. When both threads have acquired a lock and counted down the latch, they proceed past the latch barrier and attempt to acquire a second lock, but in each case the other thread already holds the desired lock. This situation results in a certain deadlock.

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class CertainDeadlock {
    static class Locker implements Runnable {
        private CountDownLatch latch;
        private Lock first, second;

        Locker(CountDownLatch latch, Lock first, Lock second) {
            this.latch = latch;
            this.first = first;
            this.second = second;
        }

        @Override
        public void run() {
            String threadName = Thread.currentThread().getName();
            try {
                first.lock();
                latch.countDown();
                System.out.println(threadName + ": locked first lock");
                latch.await();
                System.out.println(threadName + ": attempting to lock second lock");
                second.lock();
                System.out.println(threadName + ": never reached");
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }

    public static void main(final String... args) {
        CountDownLatch latch = new CountDownLatch(2);
        Lock lock1 = new ReentrantLock(), lock2 = new ReentrantLock();
        new Thread(new Locker(latch, lock1, lock2), "Thread 1").start();
        new Thread(new Locker(latch, lock2, lock1), "Thread 2").start();
    }
}
Greg Mattes
  • 33,090
  • 15
  • 73
  • 105
15

Here is a Java example by following Eric Lippert's one:

public class Lock implements Runnable {

    static {
        System.out.println("Getting ready to greet the world");
        try {
            Thread t = new Thread(new Lock());
            t.start();
            t.join();
        } catch (InterruptedException ex) {
            System.out.println("won't see me");
        }
    }

    public static void main(String[] args) {
        System.out.println("Hello World!");
    }

    public void run() {           
        Lock lock = new Lock();      
    }

}
gstackoverflow
  • 36,709
  • 117
  • 359
  • 710
Yuriy Zubarev
  • 2,821
  • 18
  • 24
  • 4
    I think using join in run method is little misleading. It suggests that this other join besides the one in static block is needed to get a deadlock while the deadlock is caused by "new Lock()" statement. My rewrite, using the static method like in C# example: http://stackoverflow.com/a/16203272/2098232 – luke657 Apr 24 '13 at 23:10
  • Could you explain your example? – gstackoverflow Jun 15 '19 at 14:24
  • according to my experiments **t.join();** inside **run()** method is redundant – gstackoverflow Jun 15 '19 at 15:11
  • I removed redundant code which prevents to understanding – gstackoverflow Jun 15 '19 at 17:49
11

Here is an Example from the documentation:

public class Deadlock {
    static class Friend {
        private final String name;
        public Friend(String name) {
            this.name = name;
        }
        public String getName() {
            return this.name;
        }
        public synchronized void bow(Friend bower) {
            System.out.format("%s: %s"
                + "  has bowed to me!%n", 
                this.name, bower.getName());
            bower.bowBack(this);
        }
        public synchronized void bowBack(Friend bower) {
            System.out.format("%s: %s"
                + " has bowed back to me!%n",
                this.name, bower.getName());
        }
    }

    public static void main(String[] args) {
        final Friend alphonse =
            new Friend("Alphonse");
        final Friend gaston =
            new Friend("Gaston");
        new Thread(new Runnable() {
            public void run() { alphonse.bow(gaston); }
        }).start();
        new Thread(new Runnable() {
            public void run() { gaston.bow(alphonse); }
        }).start();
    }
}
BoltClock
  • 700,868
  • 160
  • 1,392
  • 1,356
CloudyMarble
  • 36,908
  • 70
  • 97
  • 130
  • 2
    +1 For linking java tutorial. – mre Jan 16 '12 at 13:02
  • 4
    "it's extremely likely" is not good enough for "will surely go into deadlock" – alf Jan 16 '12 at 13:15
  • 1
    @alf Oh but the fundamental issue is demonstrated quite nicely here. One can write a Round robin scheduler which exposes an `Object invokeAndWait(Callable task)` method. Then all `Callable t1` has to do is `invokeAndWait()` for `Callable t2` during its life time before returning, and vice versa. – user268396 Jan 16 '12 at 13:37
  • 2
    @user268396 well, the fundamental issue is trivial and boring :) The whole point of the task is to find out—or prove that you understand—that it's _surprisingly hard_ to get a guaranteed deadlock (as well as to guarantee anything in an asynchronous world). – alf Jan 16 '12 at 13:45
  • 1
    You could force the thread to sleep for a second on the bow method. public synchronized void bow(Friend bower) { System.out.format("%s: %s" + " has bowed to me!%n", this.name, bower.getName()); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } bower.bowBack(this); } – bezz Jan 16 '12 at 14:48
  • 4
    @bezz `sleep` is boring. While I do believe that no thread will be starting for 5 seconds, it's a race condition, anyway. You don't want to hire a programmer who would rely on `sleep()` in resolving race conditions :) – alf Jan 16 '12 at 15:44
  • Where is your explanation? Actually it MIGHT lead to deadlock but it is not guaranteed so I downvote it – gstackoverflow Jun 15 '19 at 14:18
9

I've rewritten Yuriy Zubarev's Java version of the deadlock example posted by Eric Lippert: https://stackoverflow.com/a/9286697/2098232 to more closely resemble the C# version. If the Java's initialization block works similarily to C# static constructor and first acquires the lock we don't need another thread to also invoke the join method to get a deadlock, it only needs to invoke some static method from Lock class, like the original C# example. Resulting deadlock seems to confirm this.

public class Lock {

    static {
        System.out.println("Getting ready to greet the world");
        try {
            Thread t = new Thread(new Runnable(){

                @Override
                public void run() {
                    Lock.initialize();
                }

            });
            t.start();
            t.join();
        } catch (InterruptedException ex) {
            System.out.println("won't see me");
        }
    }

    public static void main(String[] args) {
        System.out.println("Hello World!");
    }

    public static void initialize(){
        System.out.println("Initializing");
    }

}
Radiodef
  • 37,180
  • 14
  • 90
  • 125
luke657
  • 826
  • 2
  • 11
  • 28
  • why when I comment out Lock.initialize() in the run method it doesn't deadlock? the initialize method doesn't do anything though?? – Aequitas Dec 08 '15 at 00:09
  • @Aequitas just a guess but the method could be being optimized away; not sure about how that would work with threads – Dave Cousineau Feb 22 '16 at 21:54
5

It's not a simplest interview task you can get: in my project, it paralysed a team's work for a whole day. It's very easy to make your program stop, but it's very hard to get it to the state where thread dump writes something like,

Found one Java-level deadlock:
=============================
"Thread-2":
  waiting to lock monitor 7f91c5802b58 (object 7fb291380, a java.lang.String),
  which is held by "Thread-1"
"Thread-1":
  waiting to lock monitor 7f91c6075308 (object 7fb2914a0, a java.lang.String),
  which is held by "Thread-2"

Java stack information for the threads listed above:
===================================================
"Thread-2":
    at uk.ac.ebi.Deadlock.run(Deadlock.java:54)
    - waiting to lock <7fb291380> (a java.lang.String)
    - locked <7fb2914a0> (a java.lang.String)
    - locked <7f32a0760> (a uk.ac.ebi.Deadlock)
    at java.lang.Thread.run(Thread.java:680)
"Thread-1":
    at uk.ac.ebi.Deadlock.run(Deadlock.java:54)
    - waiting to lock <7fb2914a0> (a java.lang.String)
    - locked <7fb291380> (a java.lang.String)
    - locked <7f32a0580> (a uk.ac.ebi.Deadlock)
    at java.lang.Thread.run(Thread.java:680)

So the goal would be to get a deadlock which JVM will consider a deadlock. Obviously, no solution like

synchronized (this) {
    wait();
}

will work in that sense, even though they will indeed stop forever. Relying on a race condition is not a good idea, either, as during interview you usually want to show something provably working, not something which should work most of the time.

Now, the sleep() solution is okay in a sense it's hard to imagine a situation where it doesn't work, but not fair (we're in a fair sport, aren't we?). The solution by @artbristol (mine is the same, just different objects as monitors) is nice, but long and uses the new concurrency primitives to get the threads in the the right state, which is not that much fun:

public class Deadlock implements Runnable {
    private final Object a;
    private final Object b;
    private final static CountDownLatch latch = new CountDownLatch(2);

    public Deadlock(Object a, Object b) {
        this.a = a;
        this.b = b;
    }

    public synchronized static void main(String[] args) throws InterruptedException {
        new Thread(new Deadlock("a", "b")).start();
        new Thread(new Deadlock("b", "a")).start();
    }

    @Override
    public void run() {
        synchronized (a) {
            latch.countDown();
            try {
                latch.await();
            } catch (InterruptedException ignored) {
            }
            synchronized (b) {
            }
        }
    }
}

I do recall that the synchronized-only solution fits 11..13 lines of code (excluding comments and imports), but have yet to recall the actual trick. Will update if I do.

Update: here's an ugly solution on synchronized:

public class Deadlock implements Runnable {
    public synchronized static void main(String[] args) throws InterruptedException {
        synchronized ("a") {
            new Thread(new Deadlock()).start();
            "a".wait();
        }
        synchronized ("") {
        }
    }

    @Override
    public void run() {
        synchronized ("") {
            synchronized ("a") {
                "a".notifyAll();
            }
            synchronized (Deadlock.class) {
            }
        }
    }
}

Note we replace a latch with an object monitor (using "a" as an object).

Community
  • 1
  • 1
alf
  • 8,377
  • 24
  • 45
  • Hum I think it's a fair interview task. It asks you to really understand deadlocks and locking in Java. I don't think the general idea is that hard either (make sure that both threads can only continue after both have locked their first resource), you just should just remember CountdownLatch - but as the interviewer I'd help the interviewee at that part if he could explain what exactly he needed (it's not a class that most devs ever need and you can't google for it in an interview). I would love getting such interesting questions for interviews! – Voo Jan 16 '12 at 16:50
  • @Voo at the time we were playing with it, there were no latches in JDK, so it was all by hand. And the difference between `LOCKED` and `waiting to lock` is subtle, not something you read during the breakfast. But well, you're probably right. Let me rephrase. – alf Jan 16 '12 at 17:03
4

This C# version, I guess java should be pretty similar.

static void Main(string[] args)
{
    var mainThread = Thread.CurrentThread;
    mainThread.Join();

    Console.WriteLine("Press Any key");
    Console.ReadKey();
}
gemasp
  • 59
  • 4
  • 2
    Good one! Truly the shortest C# program to create a deadlock if you remove the `console` statements. You can simply write the whole `Main` function as `Thread.CurrentThread.Join();`. – RBT Jun 07 '17 at 12:42
3
import java.util.concurrent.CountDownLatch;

public class SO8880286 {
    public static class BadRunnable implements Runnable {
        private CountDownLatch latch;

        public BadRunnable(CountDownLatch latch) {
            this.latch = latch;
        }

        public void run() {
            System.out.println("Thread " + Thread.currentThread().getId() + " starting");
            synchronized (BadRunnable.class) {
                System.out.println("Thread " + Thread.currentThread().getId() + " acquired the monitor on BadRunnable.class");
                latch.countDown();
                while (true) {
                    try {
                        latch.await();
                    } catch (InterruptedException ex) {
                        continue;
                    }
                    break;
                }
            }
            System.out.println("Thread " + Thread.currentThread().getId() + " released the monitor on BadRunnable.class");
            System.out.println("Thread " + Thread.currentThread().getId() + " ending");
        }
    }

    public static void main(String[] args) {
        Thread[] threads = new Thread[2];
        CountDownLatch latch = new CountDownLatch(threads.length);
        for (int i = 0; i < threads.length; ++i) {
            threads[i] = new Thread(new BadRunnable(latch));
            threads[i].start();
        }
    }
}

The program always deadlocks because each thread is waiting at the barrier for the other threads, but in order to await the barrier, the thread must be holding the monitor on BadRunnable.class.

Daniel Trebbien
  • 38,421
  • 18
  • 121
  • 193
2

There's an example in Java here

http://baddotrobot.com/blog/2009/12/24/deadlock/

Where a kidnapper gets into a deadlock when he refuses to give up the victim until he gets the cash but the negotiator refuses to give up the cash until he gets the victim.

Toby
  • 9,523
  • 8
  • 36
  • 59
  • That implementation is not pertinent as given. Some pieces of code appear to be missing. However the general idea you express is correct as far as resource contention leading to deadlock is concerned. – Master Chief Jan 21 '12 at 01:39
  • the example is pedagogical so I'm curious why you interpret it as not pertinent... the missing code are empty methods where the method names are supposed to be helpful (but not shown for brevity) – Toby Feb 12 '12 at 17:28
1

A simple search gave me the following code:

public class Deadlock {
    static class Friend {
        private final String name;
        public Friend(String name) {
            this.name = name;
        }
        public String getName() {
            return this.name;
        }
        public synchronized void bow(Friend bower) {
            System.out.format("%s: %s"
                + "  has bowed to me!%n", 
                this.name, bower.getName());
            bower.bowBack(this);
        }
        public synchronized void bowBack(Friend bower) {
            System.out.format("%s: %s"
                + " has bowed back to me!%n",
                this.name, bower.getName());
        }
    }

    public static void main(String[] args) {
        final Friend alphonse =
            new Friend("Alphonse");
        final Friend gaston =
            new Friend("Gaston");
        new Thread(new Runnable() {
            public void run() { alphonse.bow(gaston); }
        }).start();
        new Thread(new Runnable() {
            public void run() { gaston.bow(alphonse); }
        }).start();
    }
}

Source: Deadlock

bchetty
  • 2,231
  • 1
  • 19
  • 26
  • 3
    "it's extremely likely" is not good enough for "will surely go into deadlock" – alf Jan 16 '12 at 13:28
1

Here's sample where one thread holding lock starts another thread which wants the same lock and then starter waits until started finishes... forever:

class OuterTask implements Runnable {
    private final Object lock;

    public OuterTask(Object lock) {
        this.lock = lock;
    }

    public void run() {
        System.out.println("Outer launched");
        System.out.println("Obtaining lock");
        synchronized (lock) {
            Thread inner = new Thread(new InnerTask(lock), "inner");
            inner.start();
            try {
                inner.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

class InnerTask implements Runnable {
    private final Object lock;

    public InnerTask(Object lock) {
        this.lock = lock;
    }

    public void run() {
        System.out.println("Inner launched");
        System.out.println("Obtaining lock");
        synchronized (lock) {
            System.out.println("Obtained");
        }
    }
}

class Sample {
    public static void main(String[] args) throws InterruptedException {
        final Object outerLock = new Object();
        OuterTask outerTask = new OuterTask(outerLock);
        Thread outer = new Thread(outerTask, "outer");
        outer.start();
        outer.join();
    }
}
Victor Sorokin
  • 11,878
  • 2
  • 35
  • 51
0

Here is an example:

two threads are running , each one waiting for other to release lock

public class ThreadClass extends Thread{

String obj1,obj2;
ThreadClass(String obj1,String obj2){
    this.obj1=obj1;
    this.obj2=obj2;
    start();
}

public void run(){
    synchronized (obj1) {
        System.out.println("lock on "+obj1+" acquired");

        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("waiting for "+obj2);
        synchronized (obj2) {
            System.out.println("lock on"+ obj2+" acquired");
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    }


}

}

Running this would lead to deadlock:

public class SureDeadlock {

public static void main(String[] args) {
    String obj1= new String("obj1");
    String obj2= new String("obj2");

    new ThreadClass(obj1,obj2);
    new ThreadClass(obj2,obj1);


}

}

Praveen Kumar
  • 1,624
  • 5
  • 18
  • 21