Concurrent locks protect multiple resources

For mutex, the relationship between protected resources and locks is usually many-to-one. So how can one mutex protect multiple resources?

First, you need to distinguish whether multiple resources are related.

The resources are not associated

There is no correlation between multiple resources. For example, there is no strong correlation between the seats of the stadium and the seats of the cinema. The tickets of the football match and the ticket management of the cinema can be used for the two resources respectively.

For example, there are two kinds of services in a bank: the withdrawal of bank account balance and the change of bank account password. There is no correlation between the two resources, so you can assign a lock to manage the two resources respectively.

The code is shown below

Public class Account {// balance private Integer balance; Public final Object balanceObjct = new Object(); // Password private String password; Public final Object passwordObject = new Object(); Public void withdraw(Integer amt){synchronized (balanceObjct){if (this.balance>amt){this.balance-=amt; Public Integer getBalance(){synchronized (balanceObjct){return balance; Public void updatePassword(String newPassword){synchronized (passwordObject){this.password = newPassword; }} public String getPassword(){synchronized (passwordObject){return this.password; }}}Copy the code

Method is more than one, of course, can also be the whole this as a lock, two resources management, although this convenient, but the problem is the lock granularity is too big, two was abruptly into the resources of the parallel serial execution affects efficiency, so we should use different locks in development of protected resources to carry on the fine management, to improve performance.

Multiple resources are associated with each other

The scene is introduced into

It is relatively complicated to deal with the correlation between multiple resources. For example, in the inter-bank transfer business, account A transfers to account B, and account B needs to add 100 yuan to account A if account A loses 100 yuan.

The problem is coded as follows

Public class Account {// balance private Integer balance; // Transfer service, Amt public void Transfer (Account target, int amt){ // public synchronized void transfer( Account target, int amt){ if (this.balance > amt) { this.balance -= amt;  target.balance += amt; }}}Copy the code

It’s tempting to acquire such a business by adding the synchronized keyword to transfer to solve concurrency problems, as line 7 shows, but is that true?

Of course not. The target account has not been protected. It is used as a parameter in the Transfer method.

For specific analysis, suppose the following scenario, three accounts A,B and C have 200 yuan respectively, A transfers 100 yuan to B, B transfers 100 yuan to C, the final correct result should be A balance of 100 yuan,B balance of 200 yuan,C balance of 300 yuan.

Thread 1 transfers 100 yuan from A to B, and thread 2 transfers 100 yuan from B to C. The two threads are running on different cpus. Are thread 1 and thread 2 mutually exclusive? Obviously not thread thread 1 and 2 can be performed at the same time, thread 1 B and thread 2 read the account balance to register is the data in the 200, and the end result is the account balance is unlikely to be 200 B, 100 (the result of the thread 1 is covered by thread 2) may also be 300 (thread results covered by thread 1 of 2).

The solution

In fact, multiple associated resources to do concurrency control, the first thing that comes to mind is that the lock needs to cover all resources, can solve the problem.

Holds the same object

public class Account { private Integer balance; Private Object lock; private Account(){ } public Account(Object lock){ this.lock = lock; Public void transfer(Account target, Int amt){synchronized (lock){if (this.balance > amt){this.balance -= amt; target.balance += amt; }}}}Copy the code

The disadvantage of this method is that the same object needs to be passed in the creation of the Account object. If different objects are passed in, there will be confusion of the lock protected object, which is not good for the stability of the program and is not feasible.

Holds class template objects

You can change the lock object to Account.class, which is shared by all accounts to ensure uniqueness.

Modify the code as follows.

public class Account { private Integer balance; Public void transfer(Account target, Synchronized (account.class){if (this.balance > amt){this.balance -= amt; synchronized (account.class){this.balance -= amt; target.balance += amt; }}}}Copy the code

\