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. Condit...

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



6 December 2019, 02:56 | Views: 4494

Add new comment

For adding a comment, please log in
or create account

0 comments