How to avoid deadlock

Keywords: Programming Java

Once the concurrent program is deadlocked, we can only restart the application. The best way to solve the deadlock problem is to avoid deadlock.

 

Conditions for deadlock

  • Mutually exclusive, shared resources can only be occupied by one thread
  • Hold and wait. Thread t1 has acquired shared resource s1. When trying to acquire shared resource s2, shared resource s1 is not released.
  • Cannot preempt. Other threads cannot forcibly preempt the resource s1 occupied by thread t1.
  • Loop wait, thread t1 waits for resources occupied by thread t2, thread t2 waits for resources occupied by thread t1

 

How to avoid deadlock

For the above four conditions, as long as one of them is broken, deadlock can be avoided.

The first condition "mutual exclusion" cannot be broken, because the purpose of locking is to ensure mutual exclusion.

Three other conditions, we can try

  • Apply for all resources at one time, and destroy the "hold and wait" condition
  • When the thread occupying part of resources further applies for other resources, if the application fails, it will actively release the resources it occupies and destroy the "non preemptive" condition.
  • Apply for resources in sequence, and destroy the "circular waiting" condition

 

Use the management class to apply for all resources at one time and destroy the "occupy and wait" condition example

package constxiong.concurrency.a023;

import java.util.HashSet;
import java.util.Set;

/**
 * Test one-time application for all resources, destroy the "occupy and wait" condition example
 * @author ConstXiong
 * @date 2019-09-24 14:04:12
 */
public class TestBreakLockAndWait {

	//Single instance resource management class
	private final static Manger manager = new Manger();
	
	//Resources 1
	private static Object res1 = new Object();
	
	//Resources 2
	private static Object res2 = new Object();
	
	public static void main(String[] args) {
		new Thread(() -> {
			boolean applySuccess = false;
			while (!applySuccess) {
				//Apply to management class for res1 and res2, failed to apply, try again
				applySuccess = manager.applyResources(res1, res2);
				if (applySuccess) {
					try {
						System.out.println("Threading:" + Thread.currentThread().getName() + " Apply res1,res2 Resource success");
						synchronized (res1) {
							System.out.println("Threading:" + Thread.currentThread().getName() + " Get to res1 Resource locks");
							//Dormancy for one second
							try {
								Thread.sleep(1000);
							} catch (Exception e) {
								e.printStackTrace();
							}
							synchronized (res2) {
								System.out.println("Threading:" + Thread.currentThread().getName() + " Get to res2 Resource locks");
							}
						}
					} finally {
						manager.returnResources(res1, res2);//Restitution resources
					}
				} else {
					System.out.println("Threading:" + Thread.currentThread().getName() + " Apply res1,res2 Resource failure");
					//Failed to apply to sleep for 200ms and then try again
					try {
						Thread.sleep(200);
					} catch (Exception e) {
						e.printStackTrace();
					}
				}
			}
		}).start();
		
		new Thread(() -> {
			boolean applySuccess = false;
			while (!applySuccess) {
				//Apply to management class for res1 and res2, failed to apply, try again
				applySuccess = manager.applyResources(res1, res2);
				if (applySuccess) {
					try {
						System.out.println("Threading:" + Thread.currentThread().getName() + " Apply res1,res2 Resource success");
						synchronized (res2) {
							System.out.println("Threading:" + Thread.currentThread().getName() + " Get to res1 Resource locks");
							//Dormancy for one second
							try {
								Thread.sleep(1000);
							} catch (Exception e) {
								e.printStackTrace();
							}
							synchronized (res1) {
								System.out.println("Threading:" + Thread.currentThread().getName() + " Get to res2 Resource locks");
							}
						}
					} finally {
						manager.returnResources(res1, res2);//Restitution resources
					}
				} else {
					System.out.println("Threading:" + Thread.currentThread().getName() + " Apply res1,res2 Resource failure");
					//Failed to apply to sleep for 200ms and then try again
					try {
						Thread.sleep(200);
					} catch (Exception e) {
						e.printStackTrace();
					}
				}
			}
		}).start();
		
	}
	
}

/**
 * Resource application and return management
 * @author ConstXiong
 * @date 2019-09-24 14:10:57
 */
class Manger {
	
	//Resource storage collection
	private Set<Object> resources = new HashSet<Object>();
	
	/**
	 * Application resources
	 * @param res1
	 * @param res2
	 * @return
	 */
	synchronized boolean applyResources(Object res1, Object res2) {
		if (resources.contains(res1) || resources.contains(res1)) {
			return false;
		} else {
			resources.add(res1);
			resources.add(res2);
			return true;
		}
	}
	
	/**
	 * Restitution resources
	 * @param res1
	 * @param res2
	 */
	synchronized void returnResources(Object res1, Object res2) {
		resources.remove(res1);
		resources.remove(res2);
	}
	
}

 

The printing result is as follows: thread-1 can successfully apply for res1 and res2 locks only after thread-0 releases resources.

Thread: Thread-0 successfully applied for res1 and res2 resources
Thread: Thread-0 obtains the lock of res1 resource
Thread: Thread-1 failed to apply for res1 and res2 resources
Thread: Thread-1 failed to apply for res1 and res2 resources
Thread: Thread-1 failed to apply for res1 and res2 resources
Thread: Thread-1 failed to apply for res1 and res2 resources
Thread: Thread-1 failed to apply for res1 and res2 resources
Thread: Thread-0 obtains the lock of res2 resource
Thread: Thread-1 failed to apply for res1 and res2 resources
Thread: Thread-1 successfully applied for res1 and res2 resources
Thread: Thread-1 obtains the lock of res1 resource
Thread: Thread-1 obtains the lock of res2 resource

 

 

Use tryLock() method of Lock to release all resources due to Lock acquisition failure. Destroy the "non preemptive" condition example

package constxiong.concurrency.a023;

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

/**
 * When the thread that occupies part of the resources further applies for other resources, if the application fails, it will actively release the resources it occupies and destroy the "non preemptive" condition.
 * @author ConstXiong
 * @date 2019-09-24 14:50:51
 */
public class TestBreakLockOccupation {
	
	private static Random r = new Random(); 

	private static Lock lock1 = new ReentrantLock();
	
	private static Lock lock2 = new ReentrantLock();
	
	public static void main(String[] args) {
		new Thread(() -> {
			//Identify whether the task is completed
			boolean taskComplete = false;
			while (!taskComplete) {
				lock1.lock();
				System.out.println("Threading:" + Thread.currentThread().getName() + " Get lock lock1 Success");
				try {
					//Sleep at random to help create a deadlock environment
					try {
						Thread.sleep(r.nextInt(30));
					} catch (Exception e) {
						e.printStackTrace();
					}
					
					//Thread 0 tried to get lock2
					if (lock2.tryLock()) {
						System.out.println("Threading:" + Thread.currentThread().getName() + " Get lock lock2 Success");
						try {
							taskComplete = true;
						} finally {
							lock2.unlock();
						}
					} else {
						System.out.println("Threading:" + Thread.currentThread().getName() + " Get lock lock2 fail");
					}
				} finally {
					lock1.unlock();
				}
				
				//Sleep randomly to avoid live lock
				try {
					Thread.sleep(r.nextInt(10));
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		}).start();
		
		new Thread(() -> {
			//Identify whether the task is completed
			boolean taskComplete = false;
			while (!taskComplete) {
				lock2.lock();
				System.out.println("Threading:" + Thread.currentThread().getName() + " Get lock lock2 Success");
				try {
					//Sleep at random to help create a deadlock environment
					try {
						Thread.sleep(r.nextInt(30));
					} catch (Exception e) {
						e.printStackTrace();
					}
					
					//Thread 2 attempts to acquire lock lock1
					if (lock1.tryLock()) {
						System.out.println("Threading:" + Thread.currentThread().getName() + " Get lock lock1 Success");
						try {
							taskComplete = true;
						} finally {
							lock1.unlock();
						}
					} else {
						System.out.println("Threading:" + Thread.currentThread().getName() + " Get lock lock1 fail");
					}
				} finally {
					lock2.unlock();
				}
				
				//Sleep randomly to avoid live lock
				try {
					Thread.sleep(r.nextInt(10));
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		}).start();
	}
	
}

 

The results are as follows

Thread: Thread-0 obtains lock lock1 successfully
Thread: Thread-1 obtains lock2 successfully
Thread: Thread-1 failed to get lock lock1
Thread: Thread-1 obtains lock2 successfully
Thread: Thread-0 failed to get lock lock2
Thread: Thread-1 obtains lock lock1 successfully
Thread: Thread-0 obtains lock lock1 successfully
Thread: Thread-0 obtains lock lock2 successfully

 

 

Lock in a certain order to break the "cycle waiting" condition example

package constxiong.concurrency.a023;

/**
 * Test to apply resources in sequence, and destroy the "cycle waiting" condition
 * @author ConstXiong
 * @date 2019-09-24 15:26:23
 */
public class TestBreakLockCircleWait {

	private static Object res1 = new Object();
	
	private static Object res2 = new Object();
	
	
	public static void main(String[] args) {
		new Thread(() -> {
			Object first = res1;
			Object second = res2;
			//Compare the hashcodes of res1 and res2. If the hashCode of res1 is > res2, exchange first and second. Ensure that objects with small hashCode are locked first
			if (res1.hashCode() > res2.hashCode()) {
				first = res2;
				second = res1;
			}
			synchronized (first) {
				System.out.println("Threading:" + Thread.currentThread().getName() + "Access to resources " + first + " Lock success");
				try {
					Thread.sleep(100);
				} catch (Exception e) {
					e.printStackTrace();
				}
				synchronized(second) {
					System.out.println("Threading:" + Thread.currentThread().getName() + "Access to resources " + second + " Lock success");
				}
			}
		}).start();
		
		new Thread(() -> {
			Object first = res1;
			Object second = res2;
			//Compare the hashcodes of res1 and res2. If the hashCode of res1 is > res2, exchange first and second. Ensure that objects with small hashCode are locked first
			if (res1.hashCode() > res2.hashCode()) {
				first = res2;
				second = res1;
			}
			synchronized (first) {
				System.out.println("Threading:" + Thread.currentThread().getName() + "Access to resources " + first + " Lock success");
				try {
					Thread.sleep(100);
				} catch (Exception e) {
					e.printStackTrace();
				}
				synchronized(second) {
					System.out.println("Threading:" + Thread.currentThread().getName() + "Access to resources " + second + " Lock success");
				}
			}
		}).start();
	}
	
}

 

The results are as follows

Thread: Thread-0 obtains the resource java.lang.Object@7447157c lock successfully
Thread: Thread-0 obtains the resource java.lang.Object@7a80f45c lock successfully
Thread: Thread-1 obtains the resource java.lang.Object@7447157c lock successfully
Thread: Thread-1 obtains the resource java.lang.Object@7a80f45c lock successfully


       

Java interview question summary, there is always a stuck you!

Posted by Bullet on Wed, 16 Oct 2019 04:53:23 -0700