How can concurrent locks protect multiple resources

Posted by davieboy on Sun, 13 Feb 2022 14:39:51 +0100

Concurrent locks protect multiple resources

For mutexes, the relationship between protected resources and locks is generally many to one, so how to use a mutex to protect multiple resources?

First, you need to distinguish whether there is an association between multiple resources.

There is no association between multiple resources

There is no correlation between multiple resources, such as the seats of the stadium and the seats of the cinema. There is no strong correlation between them. The two resources can be managed by using the tickets of the ball game and the tickets of the cinema respectively.

For example, there are two kinds of businesses in a bank, namely, bank account balance withdrawal business and bank account password change business. There is no correlation between the two resources, so the two resources can be assigned to a lock management.

The code is shown below

public class Account {
    // balance
    private Integer balance;

    // Balance locking object
    public final Object balanceObjct = new Object();

    // password
    private String password;

    // Password lock object
    public final Object passwordObject = new Object();

    // withdraw money
    public void withdraw(Integer amt){
        synchronized (balanceObjct){
            if (this.balance>amt){
                this.balance-=amt;
            }
        }
    }

    // View balance
    public Integer getBalance(){
        synchronized (balanceObjct){
            return balance;
        }
    }

    // Change Password
    public void updatePassword(String newPassword){
        synchronized (passwordObject){
            this.password = newPassword;
        }
    }

    // Query password
    public String getPassword(){
        synchronized (passwordObject){
            return this.password;
        }
    }
}

Of course, there are more than one method. You can also use the whole this as a lock to manage two resources. Although it is convenient to write this, the problem is that the granularity of the lock is too large. The two originally parallel resources are forcibly turned into serial execution, which affects the efficiency. Therefore, we should use different locks to finely manage the protected resources in development, which can improve the performance.

There is an association relationship between multiple resources

Introduction scenario

The processing of the association relationship between multiple resources is relatively complex. For example, in the inter-bank transfer business, account A transfers to account B, account A is less than 100 yuan, and account B needs to add 100 yuan.

The problem is coded as follows

public class Account {
    // balance
    private Integer balance;

    // Transfer business: the current this object account transfers amt to the target account
    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; 
        } 
    }
}

When you get such a business, you usually take it for granted to add the synchronized keyword to transfer to solve the concurrency problem, as shown in line 7, but is this really the case?

Of course not. The target account has not been protected. It is used as a parameter in the transfer method. The locking operation can only be applied to the current this object, and the target object has no locking operation.

For specific analysis, suppose there are the following scenarios: accounts a, B and C have an amount of 200 yuan respectively. A transfers 100 yuan to B and B transfers 100 yuan to C. the final correct result should be that the balance of a is 100 yuan, the balance of B is 200 yuan and the balance of C is 300 yuan.

Now put the above requirements into the above code. At present, there are two threads. Thread 1 transfers 100 yuan from A to B and thread 2 transfers 100 yuan from B to C. the two threads run on different CPU s. Are thread 1 and thread 2 mutually exclusive? Obviously, thread 1 and thread 2 can not execute at the same time. The data read by thread 1 and thread 2 from the balance of account B to the register is 200. The final result is that the balance of account B can not be 200, it can be 100 (the result of thread 1 is overwritten by thread 2) or 300 (the result of thread 2 is overwritten by thread 1).

terms of settlement

In fact, when multiple associated resources are used for concurrency control, the first thing to think of is that the lock needs to cover all resources to solve the problem.

Hold the same object

public class Account {
    private Integer balance;

    // New code start
    private Object lock;

    private Account(){

    }

    public Account(Object lock){
        this.lock = lock;
    }
    // End of new code

    public void transfer( Account target, int amt){
        // Acquire lock
        synchronized (lock){
            if (this.balance > amt) {
                this.balance -= amt;
                target.balance += amt;
            }
        }
    }
}

The defect of this is that the same object needs to be passed in when creating the Account object. If different objects are passed in, the lock protection object will be disordered, which is not good for the stability of the program and lack of feasibility.

Hold class template object

You can change the lock object to Account Class, which is shared by all accounts and can determine uniqueness.

The modification code is as follows.

public class Account {
    private Integer balance;
    
    // End of new code

    public void transfer( Account target, int amt){
        // Get lock use class template
        synchronized (Account.class){
            if (this.balance > amt) {
                this.balance -= amt;
                target.balance += amt;
            }
        }
    }
}

Topics: Java security