Java concurrency-lock sequence deadlock problem

Source: Internet
Author: User
Tags comparable

The concept of first contact deadlock is the "Philosopher's meal" as described in the principle of operating system in a university course. In the operating system, a deadlock problem may occur because each process shares system resources. In the same Java multithreaded environment, there are also deadlock problems caused by resource sharing. When a set of Java threads Deadlock, it is possible that the program will block this and not end normally. In Java applications, we use a locking mechanism to secure threads, but if you use locks excessively, you can cause lock order deadlock problems.

The analysis of lock sequence deadlock in the book "Java Concurrency Programming Practice" is compiled, and the code example is verified as follows.

Lock sequence Deadlock

When you need to acquire multiple locks at the same time, the order of locking is different, which can lead to deadlock problems. The code for a simple lock sequence deadlock is as follows:

public class Leftrightdeadlock {Private Final object left = new Object ();p rivate Final Object right = new Object ();p ublic void LeftRight () {synchronized (left) {synchronized (right) {//dosomethoing ();}}} public void Rightleft () {synchronized (right) {synchronized (left) {//dosomethoing ();}}}}
The above code has a deadlock risk: when one thread calls the LeftRight method to get the left lock, and the other thread calls the Rightleft method to get the right lock, then they are stuck in a blocking process waiting for another lock, causing a deadlock. Because their operations are interleaved, and the order of locks added to the same object is interleaved, this can lead to deadlocks. The solution is that all threads acquire the same lock in a fixed order, and there is no lock order deadlock problem in the program.

Dynamic lock sequence Deadlock The above lock is a lock on a fixed member variable, you can control the order of locks, and there is a dynamic sequential deadlock in the method in which the lock behavior of the parameters passed by the method is added. According to the code of the fragment given in Java Concurrency programming practice, it describes the scenario of transferring funds from one account to another and acquiring the locks of the two account objects at the same time before starting the transfer to ensure that the balances in the two accounts are updated by atomic operation. The abstraction of this scenario requires a amount class representing the amount, an account class, and a transfer method. Write the following code:

The top level amount abstract class amount, implements the comparable for the data verification operation before the transfer; its subclass Dallaramount.

/** * Top level Amount Abstract class: contains currency and balances two properties * @author BH */public Abstract class Amount implements comparable <amount>{public abstract BigDecimal getbalance ();p ublic abstract void setbalance (BigDecimal balance);p ublic Abstract Currency getcurrency (); @Overridepublic int compareTo (Amount o) {if (o==null) {throw new NullPointerException (" Null arg. ");} if (this.getbalance () ==null| | O.getbalance () ==null) {throw new NullPointerException ("null arg."); Return This.getbalance (). CompareTo (O.getbalance ());}} 
/** * USD class * @author BH */public class Dollaramount extends Amount{private BigDecimal balance;private Currency Currency = C Urrency.getinstance (locale.us);p ublic dollaramount (BigDecimal balance) {this.balance = balance;} Public BigDecimal GetBalance () {return balance;} public void Setamount (BigDecimal amount) {this.balance = amount;} Public Currency getcurrency () {return Currency;} public void Setbalance (BigDecimal balance) {this.balance = balance;}}
Account class, which provides the method of borrowing for the accounts.

/** * Account type: Including amount, borrowing and obtaining balance method * @author BH */public class Account {private String id;private Amount balance;public accounts (Amou NT amount,string ID) {this.balance = Amount;this.id = ID;} Public Amount GetBalance () {return balance;} public void Setbalance (Amount balance) {this.balance = balance;} public void Debit (Amount Amount) {if (this.balance==null| | amount==null| | Amount.getbalance () ==null) {return;} System.out.println (id+ "Spending amount" +amount.getbalance ())//Fixed account balance: This account minus the debit amount BigDecimal current = This.balance.getBalance ( ); BigDecimal now = Current.subtract (Amount.getbalance ()), this.balance.setBalance (now); public void Credits (Amount Amount) {if (this.balance==null| | amount==null| | Amount.getbalance () ==null) {return;} System.out.println (id+ "Income Amount" +amount.getbalance ())//Fixed account balance: This account plus Credit Amount BigDecimal current = This.balance.getBalance () ; BigDecimal now = Current.add (Amount.getbalance ()), this.balance.setBalance (now); Public String GetId () {return ID;} public void SetId (String id) {this.id = id;}}
A transfer function provider that acquires a lock on two accounts before executing the transfer:

/** * Simultaneous access to two account locks * @author BH */public class Accounthelper {public void TransferMoney Toacct,final Amount Amount) {//Parameter check if (fromacct==null| | toacct==null| | Amount==null) {throw new illegalargumentexception ("null arg.");} Balance Check if (fromacct.getbalance (). CompareTo (amount) <0) {throw new IllegalArgumentException (Fromacct.getid () + " Insufficient account balance ");} Synchronized (FROMACCT) {synchronized (TOACCT) {This.transfer (FROMACCT, Toacct, amount);}}} Transfer operations on two accounts must be atomic complete private void Transfer (final account fromacct,final account toacct,final Amount Amount) { System.out.println ("Thread" +thread.currentthread (). GetName () + "do transfer."); Fromacct.debit (amount); Toacct.credit (amount);}}
The above code seems to have no problem, but it does have a deadlock problem, although the thread appears to lock the From and then lock the to account with the same lock, but in fact the order of the locks is dynamic, depending on the order of the parameters passed to the method, and the order of the parameters depends on the external input. Write the test code, and start four threads alternately executing the TransferMoney method, while the parameter pass order is in reverse order. The code is as follows:

public class Maintest {public static void main (string[] args) {Amount AMFROMACC = new Dollar Amount (New BigDecimal (2000)); Amount AMTOACC = new Dollaramount (new BigDecimal); final accounthelper h = new Accounthelper (); Final account Fromacc = new Account (AMFROMACC, "Zhang_3"), Final account TOACC = new account (AMTOACC, "Wang_5"); final Amount Amtotran = new Dollara Mount (New BigDecimal (1)); thread T1 = new Thread (new Runnable () {@Overridepublic void run () {H.transfermoney (FROMACC, TOACC, Amtotran);}); thread T4 = new Thread (new Runnable () {@Overridepublic void run () {H.transfermoney (FROMACC, TOACC, Amtotran);}); Thread t2 = new Thread (new Runnable () {@Overridepublic void run () {H.transfermoney (TOACC, FROMACC, Amtotran);}); thread t3 = new Thread (new Runnable () {@Overridepublic void run () {H.transfermoney (TOACC, FROMACC, Amtotran);}); T1.start (); T2.start (); T3.start (); T4.start ();}} 
Repeated execution of this method, the first three times it happens to be able to run normally, but the fourth time the execution, encountered a deadlock problem, only two threads in order to complete the operation, and the other two threads into a deadlock state, the program has been unable to end, the test results are as follows:



Because we cannot control the order of the parameters, we must define the order of a fixed lock so that the program's lock order is not affected by the parameters. The solution given in this book is to compare the hashcode of the objects in order to fix a sequence in which two objects have the same hash value and then use extra time locks to lock the order. The code for Accounthelper to crack dynamic sequential deadlocks is as follows:

/** * Set the order of lock acquisition according to a certain rule, avoid lock order deadlock problem * @author BH * */public class Accounthelper {//Overtime lock private final Object Tielock = new Objec T ();p ublic void TransferMoney (Final account fromacct,final account toacct,final Amount Amount) {//Parameter check if (fromacct==null || toacct==null| | Amount==null) {throw new illegalargumentexception ("null arg.");} Balance Check if (fromacct.getbalance (). CompareTo (amount) <0) {throw new IllegalArgumentException (Fromacct.getid () + " Insufficient account balance ");} The lock order is randomly set according to the object's hash value int fromhash = System.identityhashcode (FROMACCT); int tohash = System.identityhashcode (TOACCT); if (Fromhash<tohash) {synchronized (FROMACCT) {synchronized (TOACCT) {This.transfer (FROMACCT, Toacct, amount);}}} else if (Fromhash>tohash) {synchronized (TOACCT) {synchronized (FROMACCT) {This.transfer (FROMACCT, Toacct, amount);}}} else{//happens to be relative, first get overtime lock synchronized (Tielock) {synchronized (FROMACCT) {synchronized (TOACCT) {This.transfer (FROMACCT), Toacct, amount);}}}} Transfer operations on two accounts must be atomic complete private void Transfer (final account fromacct,final AcCount Toacct,final Amount Amount) {System.out.println ("Thread" +thread.currentthread (). GetName () + "do transfer."); Fromacct.debit (amount); Toacct.credit (amount);}}

The example given in the conclusion is a concise and clear analysis of the risk of excessive use of the lock, and it may give an example to explain the existence of the problem. I can't help thinking, who would write such code in the real development process? Why do you want to get multiple locks at the same time? Using overtime lock in solving dynamic lock sequence deadlock, I can only use this one lock in the beginning to guarantee the atomicity of the transfer operation.        However, I have to admit that this code broadens my horizons and programming thinking: there is still such a problem, but also can use such a solution. When the validation code encountered a deadlock, the Eclipse console always showed a red running state, and I thought it was fun at first, but then realized the threat of deadlock to the application, what if the application we put into production is experiencing this situation? After reading this chapter, the stuck test program is the most intuitive explanation for the deadlock.

Java concurrency-lock sequence deadlock problem

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.