Tips to prevent deadlocks in java

杀马特。学长 韩版系。学妹 提交于 2021-02-17 07:27:14

问题


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?


回答1:


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



回答2:


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.




回答3:


  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)



回答4:


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.




回答5:


  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.



回答6:


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 :)




回答7:


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.




回答8:


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.




回答9:


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.



回答10:


  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.




回答11:


  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




回答12:


  • Avoid Nested Locks
  • Avoid Unnecessary Locks
  • Use thread join()


来源:https://stackoverflow.com/questions/16780937/tips-to-prevent-deadlocks-in-java

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!