Java security coding guide: dead lock

brief introduction

In order to ensure the security of shared data in Java, we introduce the lock mechanism. With a lock, a deadlock may occur.

The reason for deadlock is that multiple threads lock the resources needed by each other, and then the existing resources are not released, resulting in circular waiting.

Generally speaking, if different threads do not have the same order of locking and releasing locks, deadlock is likely to occur.

Different locking sequence

Let's take an example of different locking sequences:

public class DiffLockOrder {

    private int amount;

    public DiffLockOrder(int amount){
       this.amount=amount;
    }

    public void transfer(DiffLockOrder target,int transferAmount){
        synchronized (this){
            synchronized (target){
                if(amount< transferAmount){
                    System.out.println("余额不足!");
                }else{
                    amount=amount-transferAmount;
                    target.amount=target.amount+transferAmount;
                }
            }
        }
    }
}

In the above example, we simulate a transfer process, and amount is used to represent the user balance. Transfer is used to transfer part of the amount of the current account to the target object.

In order to ensure that the two accounts will not be modified by others during the transfer process, we use two synchronized keywords to lock the transfer object and the target object respectively.

It seems that there is no problem, but we do not consider that the order of transfer can be changed during the call:

        DiffLockOrder account1 = new DiffLockOrder(1000);
        DiffLockOrder account2 = new DiffLockOrder(500);

        Runnable target1= ()->account1.transfer(account2,200);
        Runnable target2= ()->account2.transfer(account1,100);
        new Thread(target1).start();
        new Thread(target2).start();

In the above example, we defined two accounts, and then the two accounts transfer to each other. Finally, it is likely to lead to mutual locking and deadlock.

Using private class variables

There will be a problem of order when using two syncs. Is there a way to synchronize all instances with just one sync?

Yes, we can use private class variables, because class variables are shared in all instances. One sync is enough:

public class LockWithPrivateStatic {

    private int amount;

    private static final Object lock = new Object();

    public LockWithPrivateStatic(int amount){
       this.amount=amount;
    }

    public void transfer(LockWithPrivateStatic target,int transferAmount){
        synchronized (lock) {
            if (amount < transferAmount) {
                System.out.println("余额不足!");
            } else {
                amount = amount - transferAmount;
                target.amount = target.amount + transferAmount;
            }
        }
    }
}

Use the same order

The reason why we have deadlock is that we can't control the locking sequence. If we can control the locking sequence, will there be no deadlock?

With this idea in mind, we add an ID field to the object:

    private final long id; // 唯一ID,用来排序
    private static final AtomicLong nextID = new AtomicLong(0); // 用来生成ID

    public DiffLockWithOrder(int amount){
       this.amount=amount;
        this.id = nextID.getAndIncrement();
    }

When initializing objects, we use static's atomiclong class to generate a unique ID for each object.

When transferring, we first compare the ID size of the two objects, then sort them according to the ID, and finally lock them in the installation order. This ensures order and avoids deadlocks.

    public void transfer(DiffLockWithOrder target,int transferAmount){
        DiffLockWithOrder fist,second;

        if (compareTo(target) < 0) {
            fist = this;
            second = target;
        } else {
            fist = target;
            second = this;
        }

        synchronized (fist){
            synchronized (second){
                if(amount< transferAmount){
                    System.out.println("余额不足!");
                }else{
                    amount=amount-transferAmount;
                    target.amount=target.amount+transferAmount;
                }
            }
        }
    }

Release the occupied lock

A deadlock is a lock that each other requests the other party to occupy, but the other party's lock has not been released. Let's consider whether the automatic release of the occupied lock can also solve the deadlock problem if the lock cannot be obtained?

Because reentrantlock has a trylock () method, we can use this method to determine whether the lock can be obtained. If not, release the occupied lock.

We use reentrantlock to complete this example:

public class DiffLockWithreentrantlock {

    private int amount;
    private final Lock lock = new reentrantlock();

    public DiffLockWithreentrantlock(int amount){
        this.amount=amount;
    }

    private void transfer(DiffLockWithreentrantlock target,int transferAmount)
            throws InterruptedException {
        while (true) {
            if (this.lock.tryLock()) {
                try {
                    if (target.lock.tryLock()) {
                        try {
                            if(amount< transferAmount){
                                System.out.println("余额不足!");
                            }else{
                                amount=amount-transferAmount;
                                target.amount=target.amount+transferAmount;
                            }
                            break;
                        } finally {
                            target.lock.unlock();
                        }
                    }
                } finally {
                    this.lock.unlock();
                }
            }
            //随机sleep一定的时间,保证可以释放掉锁
            Thread.sleep(1000+new Random(1000L).nextInt(1000));
        }
    }

}

We put the two trylock methods in the while loop. If we can't get the lock, we iterate through the loop.

Code for this article:

learn-java-base-9-to-20/tree/master/security

The content of this article comes from the network collection of netizens. It is used as a learning reference. The copyright belongs to the original author.
THE END
分享
二维码
< <上一篇
下一篇>>