How do I avoid deadlocks?

When concurrent programs become deadlocked, we often have to restart the application.The best way to solve the deadlock problem is to avoid it.

 

Conditions under which deadlocks occur

  • Mutually exclusive, shared resources can only be occupied by one thread
  • Occupy and wait, thread t1 has acquired shared resource s1, try to acquire shared resource s2, do not release shared resource S1
  • Not preemptive, other threads cannot forcibly preempt resource s1 held by thread t1
  • Loop wait, thread t1 waits for resources occupied by thread t2, thread T2 waits for resources occupied by thread t1

 

Ways to avoid deadlocks

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

Mutual exclusion cannot be destroyed for the first condition, because locks are designed to guarantee mutual exclusion.

Three other conditions, we can try

  • Apply for all resources at once, breaking the "possess and wait" condition
  • When a thread that owns part of a resource requests another resource further, unless it can do so, it actively releases the resource it owns and breaks the non-preemptive condition
  • Apply resources sequentially, breaking the "circular wait" condition

 

Examples of using management to apply for all resources at once, destroying the "possess and wait" condition

package constxiong.concurrency.a023;

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

/**
 * Examples of testing a one-time request for all resources, breaking the Occupy and Wait condition
 * @author ConstXiong
 * @date 2019-09-24 14:04:12
 */
public class TestBreakLockAndWait {

	//Singleton Resource Management Class
	private final static Manger manager = new Manger();
	
	//Resource 1
	private static Object res1 = new Object();
	
	//Resource 2
	private static Object res2 = new Object();
	
	public static void main(String[] args) {
		new Thread(() -> {
			boolean applySuccess = false;
			while (!applySuccess) {
				//Apply res1 and res2 to management class, failed, retry
				applySuccess = manager.applyResources(res1, res2);
				if (applySuccess) {
					try {
						System.out.println("Threads:" + Thread.currentThread().getName() + " Apply res1,res2 Resource Success");
						synchronized (res1) {
							System.out.println("Threads:" + Thread.currentThread().getName() + " Get res1 Locks on resources");
							//Hibernate for 1 second
							try {
								Thread.sleep(1000);
							} catch (Exception e) {
								e.printStackTrace();
							}
							synchronized (res2) {
								System.out.println("Threads:" + Thread.currentThread().getName() + " Get res2 Locks on resources");
							}
						}
					} finally {
						manager.returnResources(res1, res2);//Return of resources
					}
				} else {
					System.out.println("Threads:" + Thread.currentThread().getName() + " Apply res1,res2 Resource Failure");
					//Failed application hibernates 200 milliseconds and retries
					try {
						Thread.sleep(200);
					} catch (Exception e) {
						e.printStackTrace();
					}
				}
			}
		}).start();
		
		new Thread(() -> {
			boolean applySuccess = false;
			while (!applySuccess) {
				//Apply res1 and res2 to management class, failed, retry
				applySuccess = manager.applyResources(res1, res2);
				if (applySuccess) {
					try {
						System.out.println("Threads:" + Thread.currentThread().getName() + " Apply res1,res2 Resource Success");
						synchronized (res2) {
							System.out.println("Threads:" + Thread.currentThread().getName() + " Get res1 Locks on resources");
							//Hibernate for 1 second
							try {
								Thread.sleep(1000);
							} catch (Exception e) {
								e.printStackTrace();
							}
							synchronized (res1) {
								System.out.println("Threads:" + Thread.currentThread().getName() + " Get res2 Locks on resources");
							}
						}
					} finally {
						manager.returnResources(res1, res2);//Return of resources
					}
				} else {
					System.out.println("Threads:" + Thread.currentThread().getName() + " Apply res1,res2 Resource Failure");
					//Failed application hibernates 200 milliseconds and retries
					try {
						Thread.sleep(200);
					} catch (Exception e) {
						e.printStackTrace();
					}
				}
			}
		}).start();
		
	}
	
}

/**
 * Resource Application, Return Management Class
 * @author ConstXiong
 * @date 2019-09-24 14:10:57
 */
class Manger {
	
	//Resource Storage Collection
	private Set<Object> resources = new HashSet<Object>();
	
	/**
	 * Request 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;
		}
	}
	
	/**
	 * Return of resources
	 * @param res1
	 * @param res2
	 */
	synchronized void returnResources(Object res1, Object res2) {
		resources.remove(res1);
		resources.remove(res2);
	}
	
}

 

The results are as follows: Thread-1 cannot successfully apply for locks on res 1 and res2 until thread-0 has released resources

Thread: Thread-0 successfully requested res1, res2 resources
Thread: Thread-0 acquires a lock on res1 resources
Thread: Thread-1 failed to request res1, res2 resources
Thread: Thread-1 failed to request res1, res2 resources
Thread: Thread-1 failed to request res1, res2 resources
Thread: Thread-1 failed to request res1, res2 resources
Thread: Thread-1 failed to request res1, res2 resources
Thread: Thread-0 acquires a lock on res2 resources
Thread: Thread-1 failed to request res1, res2 resources
Thread: Thread-1 successfully requested res1, res2 resources
Thread: Thread-1 acquires a lock on res1 resources
Thread: Thread-1 acquires a lock on res2 resources

 

 

Using Lock's tryLock() method to get a lock fails to release all resources, breaking 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 testing a thread that owns part of a resource for further requests for other resources, unless it can do so, actively release the resource it owns, breaking the "no preemption" 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("Threads:" + Thread.currentThread().getName() + " Acquire locks lock1 Success");
				try {
					//Random hibernation helps create deadlocked environments
					try {
						Thread.sleep(r.nextInt(30));
					} catch (Exception e) {
						e.printStackTrace();
					}
					
					//Thread 0 tries to get lock2
					if (lock2.tryLock()) {
						System.out.println("Threads:" + Thread.currentThread().getName() + " Acquire locks lock2 Success");
						try {
							taskComplete = true;
						} finally {
							lock2.unlock();
						}
					} else {
						System.out.println("Threads:" + Thread.currentThread().getName() + " Acquire locks lock2 fail");
					}
				} finally {
					lock1.unlock();
				}
				
				//Random hibernation to avoid live locks
				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("Threads:" + Thread.currentThread().getName() + " Acquire locks lock2 Success");
				try {
					//Random hibernation helps create deadlocked environments
					try {
						Thread.sleep(r.nextInt(30));
					} catch (Exception e) {
						e.printStackTrace();
					}
					
					//Thread 2 tried to acquire lock lock1
					if (lock1.tryLock()) {
						System.out.println("Threads:" + Thread.currentThread().getName() + " Acquire locks lock1 Success");
						try {
							taskComplete = true;
						} finally {
							lock1.unlock();
						}
					} else {
						System.out.println("Threads:" + Thread.currentThread().getName() + " Acquire locks lock1 fail");
					}
				} finally {
					lock2.unlock();
				}
				
				//Random hibernation to avoid live locks
				try {
					Thread.sleep(r.nextInt(10));
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		}).start();
	}
	
}

 

The printout is as follows

Thread: Thread-0 acquired lock lock1 successfully
Thread: Thread-1 acquired lock lock2 successfully
Thread: Thread-1 failed to acquire lock lock1
Thread: Thread-1 acquired lock lock2 successfully
Thread: Thread-0 failed to acquire lock lock2
Thread: Thread-1 acquired lock lock1 successfully
Thread: Thread-0 acquired lock lock1 successfully
Thread: Thread-0 acquired lock lock2 successfully

 

 

Examples of breaking the "cyclic wait" condition by locking in a certain order

package constxiong.concurrency.a023;

/**
 * Test ordered requests for resources, breaking "circular wait" conditions
 * @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, swap first and second if res1's hashcode > res2.Ensure that small hashCode objects are locked first
			if (res1.hashCode() > res2.hashCode()) {
				first = res2;
				second = res1;
			}
			synchronized (first) {
				System.out.println("Threads:" + Thread.currentThread().getName() + "Get Resources " + first + " Lock Success");
				try {
					Thread.sleep(100);
				} catch (Exception e) {
					e.printStackTrace();
				}
				synchronized(second) {
					System.out.println("Threads:" + Thread.currentThread().getName() + "Get Resources " + second + " Lock Success");
				}
			}
		}).start();
		
		new Thread(() -> {
			Object first = res1;
			Object second = res2;
			//Compare the hashCodes of res1 and res2, swap first and second if res1's hashcode > res2.Ensure that small hashCode objects are locked first
			if (res1.hashCode() > res2.hashCode()) {
				first = res2;
				second = res1;
			}
			synchronized (first) {
				System.out.println("Threads:" + Thread.currentThread().getName() + "Get Resources " + first + " Lock Success");
				try {
					Thread.sleep(100);
				} catch (Exception e) {
					e.printStackTrace();
				}
				synchronized(second) {
					System.out.println("Threads:" + Thread.currentThread().getName() + "Get Resources " + second + " Lock Success");
				}
			}
		}).start();
	}
	
}

 

The printout is as follows

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


 

 

All Resource Resources Summarized on Public Number



 

Tags: Programming Java Hibernate

Posted on Fri, 06 Dec 2019 02:56:43 -0500 by arth