Java multithreaded deadlock transfer case

Article Directory

Deadlock demo of two transfers

Two locks are required: when transferring money, lock your account, and only one thread can perform the transfer at the same time. If
two locks are successfully obtained, and the balance is greater than 0, the transferor's money will be deducted and the recipient's money will be increased. And it is an atomic operation.

There is a deadlock situation: the other party transfers money to me, and I also transfer money to the other party, then both parties hold their own locks and need the other party's lock, which causes a deadlock.

The following code demonstrates the occurrence of transfer deadlock.
In the run method, different transfer methods are executed according to the flags of different threads, and a and b are transferred to each other. The
transferMoney method is the transfer method.
In the transfer method, respectively To obtain the two locks of the transferor and the recipient, the transfer can be performed.

package com.thread.deadlock;

/**
 * 类名称:TransferMoney
 * 类描述: 转账时遇到死锁的代码演示
 *
 * @author: https://javaweixin6.blog.csdn.net/
 * 创建时间:2020/9/8 19:24
 * Version 1.0
 */
public class TransferMoney implements Runnable {
    
    

    //根据不同的flag, 给不同的人转账
    int flag = 1 ;

    static Account a = new Account(500);
    static Account b = new Account(500);

    public static void main(String[] args) throws InterruptedException {
    
    
        TransferMoney r1 = new TransferMoney();
        TransferMoney r2 = new TransferMoney();
        r1.flag = 1;
        r2.flag = 0;

        Thread thread1 = new Thread(r1);
        Thread thread2 = new Thread(r2);

        thread1.start();
        thread2.start();

        //主线程等待子线程执行完毕
        thread1.join();
        thread2.join();

        System.out.println("a的余额 " +a.balance);
        System.out.println("b的余额 " +b.balance);

    }

    @Override
    public void run() {
    
    
        //flag 是1 则 a 给b钱
        if (flag == 1) {
    
    
            transferMoney(a, b, 200);
        }
        //flag 是0  则b 给a钱
        if (flag == 0) {
    
    
            transferMoney(b, a, 200);
        }
    }

    /**
     *  转账的方法
     * @param from 转账方
     * @param to  收账方
     * @param amount  金额
     */
    public static void transferMoney(Account from, Account to, int amount) {
    
    
        synchronized (from) {
    
    
            synchronized (to) {
    
    
                //转账前判断余额是否充足
                if (from.balance - amount < 0) {
    
    
                    System.out.println("余额不足, 转账失败"+Thread.currentThread().getName());
                    return;
                }

                //余额充足,则进行转账操作. 转账方扣钱,  收款方收钱
                from.balance -=amount;
                to.balance +=amount;
                System.out.println("成功转账" +amount +"元 "+Thread.currentThread().getName());
            }
        }

    }

    //账户的静态内部类
    static class Account {
    
    
        //余额
        int balance;

        public Account(int balance) {
    
    
            this.balance = balance;
        }
    }

}

Run the program, and the transfer operation is successfully executed at this time. The threads named 0 and 1 respectively go to the transfers performed in the transfer method, and the balance is unchanged because they transfer 200 to each other.

Modify as follows:

package com.thread.deadlock;

/**
 * 类名称:TransferMoney
 * 类描述: 转账时遇到死锁的代码演示
 *
 * @author: https://javaweixin6.blog.csdn.net/
 * 创建时间:2020/9/8 19:24
 * Version 1.0
 */
public class TransferMoney implements Runnable {
    
    

    //根据不同的flag, 给不同的人转账
    int flag = 1 ;

    static Account a = new Account(500);
    static Account b = new Account(500);

    public static void main(String[] args) throws InterruptedException {
    
    
        TransferMoney r1 = new TransferMoney();
        TransferMoney r2 = new TransferMoney();
        r1.flag = 1;
        r2.flag = 0;

        Thread thread1 = new Thread(r1);
        Thread thread2 = new Thread(r2);

        thread1.start();
        thread2.start();

        //主线程等待子线程执行完毕
        thread1.join();
        thread2.join();

        System.out.println("a的余额 " +a.balance);
        System.out.println("b的余额 " +b.balance);

    }

    @Override
    public void run() {
    
    
        //flag 是1 则 a 给b钱
        if (flag == 1) {
    
    
            transferMoney(a, b, 200);
        }
        //flag 是0  则b 给a钱
        if (flag == 0) {
    
    
            transferMoney(b, a, 200);
        }
    }

    /**
     *  转账的方法
     * @param from 转账方
     * @param to  收账方
     * @param amount  金额
     */
    public static void transferMoney(Account from, Account to, int amount) {
    
    
        synchronized (from) {
    
    
            try {
    
    
                Thread.sleep(20);
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }

            synchronized (to) {
    
    
                //转账前判断余额是否充足
                if (from.balance - amount < 0) {
    
    
                    System.out.println("余额不足, 转账失败"+Thread.currentThread().getName());
                    return;
                }

                //余额充足,则进行转账操作. 转账方扣钱,  收款方收钱
                from.balance -=amount;
                to.balance +=amount;
                System.out.println("成功转账" +amount +"元 "+Thread.currentThread().getName());
            }
        }

    }

    //账户的静态内部类
    static class Account {
    
    
        //余额
        int balance;

        public Account(int balance) {
    
    
            this.balance = balance;
        }
    }
}

Modified that before acquiring the second lock, give the current thread sleep for 20ms.

At this time, run the program, you can see that nothing is printed on the console, and the red button is always on, indicating that it has entered a deadlock state.
Both threads Only execute to the 67th line of the above figure, the code cannot be executed.

Analysis of the cause of the deadlock. After the first thread enters the following method, it obtains the lock from from, then sleeps, enters the timed_waiting state, and because it is sleep, does not release the lock from from.
At this time, the CPU schedules and executes the second thread, and the from of the second thread is the to lock of the first thread. After the second thread obtains the to lock, it also enters the timed_waiting state , The to lock will not be released.
Then the CPU switches back to the first thread and wants to acquire the to lock, but it cannot be acquired because it is held by the second thread. The
second thread holds the to lock and wants If you can't get the from lock, you will enter the deadlock state.

The reason for the deadlock is that in the following method, the sequence of lock acquisition is reversed.

Supplement: The above demo code is created for two account classes All are static objects, and static objects have only one instance in the entire Java virtual machine, so when different threads acquire a lock, only one thread always acquires the lock. This situation applies to a single project. And distributed The situation of obtaining a lock is different, and another method is needed to deal with it.

Guess you like

Origin blog.csdn.net/qq_33229669/article/details/108475207