51

I am studying java threads and deadlocks, I understand deadlock's examples but I wonder if there are general rules to follow to prevent it.

My question is if there are rules or tips that can be applied to the source code in java to prevent deadlocks? If yes, could you explain how to implement it?

djechlin
  • 59,258
  • 35
  • 162
  • 290
iberck
  • 2,372
  • 5
  • 31
  • 41

13 Answers13

40

Some quick tips out of my head

  • don't use multiple threads (like Swing does, for example, by mandating that everything is done in the EDT)
  • don't hold several locks at once. If you do, always acquire the locks in the same order
  • don't execute foreign code while holding a lock
  • use interruptible locks
JB Nizet
  • 678,734
  • 91
  • 1,224
  • 1,255
  • point (1):ok, like EDT. (2): How can I adquire the threads in the same order? (3):OK, I understand this point 4:What is interruptible locks? Thanks – iberck May 27 '13 at 22:10
  • @iberck (4): http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/locks/Lock.html#lockInterruptibly%28%29 – assylias May 27 '13 at 22:14
  • Also checkout Sun's SafeLock trail: http://docs.oracle.com/javase/tutorial/essential/concurrency/newlocks.html – BrianH May 28 '13 at 03:49
  • 6
    Can you clarify what you mean by *"foreign"* code? Code that makes network calls? Calls that block? Calls to other classes not written by you? Calls to code written in other countries :) ... – Nate May 28 '13 at 08:06
  • 1
    Calls to classes not written by you (like a listener, observer, strategy, provided by the caller of your API). – JB Nizet May 28 '13 at 08:42
  • 1
    The reason we may not execute foreign code while holding a lock is that if the foreign code calls some part of your code A back, then there is a risk of that part A trying to request your lock again, and then your thread will be stuck in a deadlock. This may be the case of what is known as 'self-locking'. – zafar142003 Mar 10 '16 at 18:39
  • @zafar142003 that won't happen in Java: all locaks are reentrant. So if you already hold a lock and try to reacquire it, there won't be any problem. – JB Nizet Mar 10 '16 at 20:04
  • Actually I was talking in general (not just Java). So, apart from this - conceding your point that in Java locks (atleast the synchronized construct) are reentrant- why would we not call some foreign code while holding a lock? – zafar142003 Mar 10 '16 at 20:09
  • @JBNizet I don't think re-entrant solve the problem if, in general, we cannot assume foreign code is single-threaded, executing on the same thread as the caller. – kftse Nov 18 '18 at 07:38
17

Encapsulate, encapsulate, encapsulate! Probably the most dangerous mistake you can make with locks is exposing your lock to the world (making it public). There is no telling what can happen if you do this as anyone would be able to acquire the lock without the object knowing (this is also why you shouldn't lock this). If you keep your lock private then you have complete control and this makes it more manageable.

Despertar
  • 21,627
  • 11
  • 81
  • 79
13
  1. Avoid locks by using lock-free data structures (e.g. use a ConcurrentLinkedQueue instead of a synchronized ArrayList)
  2. Always acquire the locks in the same order, e.g. assign a unique numerical value to each lock and acquire the locks with lower numerical value before acquiring the locks with higher numerical value
  3. Release your locks after a timeout period (technically this doesn't prevent deadlocks, it just helps to resolve them after they've occurred)
Zim-Zam O'Pootertoot
  • 17,888
  • 4
  • 41
  • 69
  • 2
    A lock-free data structure cannot cause deadlock. The [ConcurrentLinkedQueue](http://docs.oracle.com/javase/6/docs/api/java/util/concurrent/ConcurrentLinkedQueue.html) is an example of a lock-free data structure - you can `offer` and `poll` the queue from multiple threads without needing to synchronize access to it – Zim-Zam O'Pootertoot May 28 '13 at 17:08
10

Read and understand Java: Concurrency and Practice. This isn't about "tips" to avoid deadlock. I would never hire a developer who knew a few tips to avoid deadlock and often avoided deadlock. It's about understanding concurrency. Fortunately there is a comprehensive intermediate-level book on the topic, so go read it.

djechlin
  • 59,258
  • 35
  • 162
  • 290
9
  1. Don't use locks.
  2. If you must, keep your locks local. Global locks can be really tricky.
  3. Do as little as possible when you hold the lock.
  4. Use stripes to only lock segments of your data
  5. Prefer Immutable types. Many times this means copying data instead of sharing data.
  6. Use compare and set (CAS) mechanics instead, See AtomicReference for example.
jontejj
  • 2,800
  • 1
  • 25
  • 27
6

Boolean flag example

I love this example. It starts two threads that share a boolean flag:

public class UntilYouUpdateIt 
{
    public static boolean flag = true;

    public static void main(String[] args) throws InterruptedException 
    {
        Thread t1 = new Thread(()->
        {
            while(flag){}
            System.out.println("end");
        });
        t1.start();

        Thread.sleep(100);

        Thread t2 = new Thread(()->
        {
           flag = false;
           System.out.println("changed");
        });
        t2.start();
      }
}

First thread will loop until flag is false, which happens in the first line of the 2nd thread. The program won't ever finish, and it's ouput would be:

changed

2nd thread dies, meanwhile the 1st one will loop forever.

Why is it happenning? Compiler opmitizations. Thread1 will never check again flag's value, as:

  • the operation inside the loop is cheap (nothing to do)
  • The compiler knows there's no other entity that can modify the flag value (as the first thread doesn't, and the 2nd one is already dead). So it assumes flag will always be true.

In other words, Thread1 will always be reading the flag value from the cache, where it is set as true.


Two ways to solve/test this:

    Thread t1 = new Thread(()->
    {
        while(flag)
        {
           System.out.print("I'm loopinnnng");
        }
        System.out.println("end");
    });

If some "heavy" operation is included (int i=1 or similar would'nt work neither), such as a System call, the optimizer will be a little more careful, checking the flag boolean in order to know if he's not wasting resources. The output would be:

I'm loopinnnng
I'm loopinnnng
I'm loopinnnng
I'm loopinnnng
I'm loopinnnng
(....)
changed
end

or

I'm loopinnnng
I'm loopinnnng
I'm loopinnnng
I'm loopinnnng
I'm loopinnnng
(....)
end
changed

Depending which thread was assigned the cpu time at the end.

The correct solution to avoid these kind of deadlocks, when working with boolean variables, should be including the volatile keyword.

volatile tells the compiler: do not try to optimize when this variable is involved.

So, this same code with just that keyword added:

public class UntilYouUpdateIt 
{
    public static volatile boolean flag = true;

    public static void main(String[] args) throws InterruptedException 
    {
        Thread t1 = new Thread(()->
        {
            while(flag){}
            System.out.println("end");
        });
        t1.start();

        Thread.sleep(100);

        Thread t2 = new Thread(()->
        {
           flag = false;
           System.out.println("changed");
        });
        t2.start();
      }
}

Will output:

changed
end

or

end
changed

The result is both threads finishing correctly, avoiding any deadlock.


Unordered locks example

This one is a basic one:

public void  methodA() 
{
  //...

  synchronized(lockA)
  {
     //...
 
     synchronized(lockB)
     {
      //...
     }
   }
} 


public void methodB() 
{
  //...

  synchronized(lockB)
  {
     //...
  
    synchronized(lockA)
     {
      //...
     }
   }
}

This methods would probably create a great deadlock if called by many threads. This is because the objects are locked in different order. This is one of the most common reasons of deadlocks, so if you want to avoid them, be sure that the locks are aquired in order.

aran
  • 10,978
  • 5
  • 39
  • 69
  • 1
    The best example of this problem is two bank accounts trying to transfer money to each other at the same time. If the locks are static it's very easy to see when they happen out of order; if they're dynamic and the data can happen to be interchanged, not so much. – djechlin May 27 '13 at 22:50
5

There is pretty much just one big rule when it comes to preventing deadlocks:

If you need to have multiple locks in your code, make sure everyone always acquire them in the same order.

Keeping your code free from locks should pretty much always be your goal though. You can try to get rid of them by using immutable or thread-local objects and lock-free data structures.

Keppil
  • 45,603
  • 8
  • 97
  • 119
  • What does "in the same order" means? do you have an example? Thanks – iberck May 27 '13 at 22:05
  • @iberck See this typical example: http://stackoverflow.com/a/1385876/829571 (it's in pseudo code but you should get the idea). – assylias May 27 '13 at 22:12
  • Sure but the "if you need" part is where the work happens 75% of the time. You should be well versed in lock free patterns. – djechlin May 27 '13 at 22:49
5

Given a design choice, use message-passing where there only locks are in the queue push/pop. This is not always possible but, if it is, you will have very few deadlocks. You can still get them, but you have to try really hard :)

Martin James
  • 24,453
  • 3
  • 36
  • 60
  • 1
    seconded. I can recommend JCSP, which formalises it nicely. See the page on Wikipedia, http://en.wikipedia.org/wiki/JCSP. CSP means that in general you can worry a lot less about deadlock, live lock, etc. As the Wiki article points out, not using CSP inevitably leaves you with a program that cannot be shown to be correct by mere testing. However, with CSP there's some simple guidelines to follow and you're inevitably OK. If you're brave you can do CSP math and prove that your program is correct with no testing. See http://en.wikipedia.org/wiki/Communicating_sequential_processes. – bazza May 28 '13 at 05:32
  • Oh, and a useful side effect of JCSP is that you've got scalability built right into your program from the outset. A thread can become a separate process on a separate computer, but the overall architecture of your program remains unchanged. So scaling from a single machine to a whole data centre's worth becomes doable with no re-design. – bazza May 28 '13 at 05:34
  • @bazza - yeah, I have used message-passing designs, communicating objects around on P-C queues, for decades on Java, Delphi and C++. On the two occasions that I have managed to get a deadlock, one was just gross stupidity on my part, the other was my bad design that overloaded a GUI input queue. – Martin James May 28 '13 at 06:20
2

Deadlock in java is a programming situation where two or more threads are blocked forever. Java deadlock situation arises with at least two threads and two or more resources.

How to Detect Deadlock in Java

To detect a deadlock in Java, we need to look at the java thread dump of the application, we can generate thread dump using VisualVM profiler or using jstack utility.

For analyzing deadlock, we need to look out for the threads with the state as BLOCKED and then the resources it’s waiting to lock. Every resource has a unique ID using which we can find which thread is already holding the lock on the object.

How to avoid deadlock in java

These are some of the guidelines using which we can avoid most of the deadlock situations.

  • Avoid deadlock by breaking circular wait condition: In order to do that, you can make arrangement in the code to impose the ordering on acquisition and release of locks. If lock will be acquired in a consistent order and released in just opposite order, there would not be a situation where one thread is holding a lock which is acquired by other and vice-versa.
  • Avoid Nested Locks: This is the most common reason for deadlocks, avoid locking another resource if you already hold one. It’s almost impossible to get a deadlock situation if you are working with only one object lock.
  • Lock Only What is Required: You should acquire lock only on the resources you have to work on, if we are only interested in one of its fields, then we should lock only that specific field not complete object.
  • Avoid waiting indefinitely: You can get a deadlock if two threads are waiting for each other to finish indefinitely using thread join. If your thread has to wait for another thread to finish, it’s always best to use join with maximum time you want to wait for the thread to finish.
amitkumar12788
  • 797
  • 3
  • 10
  • 14
1
  1. Avoid nested locks. This is the most common reason for deadlock. Avoid locking another resource if you already hold one.It is almost impossible to get deadlock if you are working with only one object lock.

  2. Lock only what is required. Like lock particular field of object instead of locking whole object if it serves your purpose.

  3. Do not wait indefinitely.

027
  • 1,553
  • 13
  • 23
1
  1. Unless required, do not share data over multiple threads. If data can't be changed after creation/initialization, stick to final variables.
  2. If you can't avoid shared data among multiple threads, use granular synchronized blocks or Locks.
  3. If you are using only synchronized code blocks, make sure that locks are acquired/released in a certain order.
  4. Look out for other alternatives : volatile or AtomicXXX variables or Lock API

Related SE questions:

Avoid synchronized(this) in Java?

Difference between volatile and synchronized in Java

Volatile boolean vs AtomicBoolean

Community
  • 1
  • 1
Ravindra babu
  • 37,698
  • 11
  • 250
  • 211
1
  • Avoid Nested Locks
  • Avoid Unnecessary Locks
  • Use thread join()
Arun Raaj
  • 1,762
  • 1
  • 21
  • 20
0

Unfortunately, there are data models with circular data flow (A->B->C->A) across multiple threads. All said above doesn't work for it. From what might help, a data transfer method between threads could use a lockless buffered input like java concurrency collections. And data chunks itself are implemented as stateless immutable objects. A GUI systems highly likely use this approach. This way a "lockfull" wait-notify scheme appears not needed, however thread concurrency control still exists but it is hidden inside collections and this way is local. The problem really is hardly avoidable. The roots originate from zero latency assumed by math model. But in real system every action takes time, so it creates latency and finally leads to deadlocks.