欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

了解多线程中的死锁及减少死锁的方法

程序员文章站 2023-01-29 18:17:50
了解死锁死锁是如何产生的?通过控制锁顺序避免死锁尝试定时的锁死锁是如何产生的?介绍:1.当一个线程永远占有一个锁,而其他线程去尝试获取该锁,它们将永远被阻塞.2.如果一个线程A占有锁A时,想要获得锁B,同时,线程锁B占有锁B,想要获得锁A,两个线程将永远等待下去,这就导致了死锁的产生.3.示例:/** * TODO * 测试实体 * * @author 86182 * @version V1.0 * @since 2020-11-27 18:17 */public class...

死锁是如何产生的?

介绍:
1.当一个线程永远占有一个锁,而其他线程去尝试获取该锁,它们将永远被阻塞.
2.如果一个线程A占有锁A时,想要获得锁B,同时,线程锁B占有锁B,想要获得锁A,两个线程将永远等待下去,这就导致了死锁的产生.


3.示例:

/**
 * TODO
 * 测试实体
 *
 * @author 86182
 * @version V1.0
 * @since 2020-11-27 18:17
 */
public class Account {
//示例死锁方法
public static void transferMoney(Account fromAccount, Account toAccount) {
        try {
            synchronized (fromAccount) {
                Thread.sleep(10);

                synchronized (toAccount) {
                    Thread.sleep(10);
                }
            }
        } catch (Exception e) {

        }
    }
}

测试类:

public class TestAccount {

    public static void main(String[] args) {
        Account fromAccount = new Account();
        Account toAccount = new Account();

        Thread threadA = new Thread(new Runnable() {
            @Override
            public void run() {
                Account.transferMoney(fromAccount, toAccount);
            }
        }, "threadA");

        Thread threadB = new Thread(new Runnable() {
            @Override
            public void run() {
                Account.transferMoney(toAccount, fromAccount);
            }
        }, "threadB");

        threadA.start();
        threadB.start();
    }

}

结果:
了解多线程中的死锁及减少死锁的方法
会发现程序没有停止,我们通过jps查看当前进程,
了解多线程中的死锁及减少死锁的方法
然后通过 jstack查看进程的线程状态:
了解多线程中的死锁及减少死锁的方法
1.得知两个线程都处于阻塞状态,都在等待对方释放锁,导致了死锁.
2.在实际的开发中,死锁是我们难以发现的,这种情况是偶发行为.难以测试.
3.安全性极高的数据处理,如银行转账就有可能写类似的代码,
两个线程同时调用transferMoney(Account fromAccount, Account toAccount)
方法,一个A向B转账,一个B向A转账,就会发生死锁.


通过控制锁顺序避免死锁

1.可以通过控制锁的顺序,通过获取对象hashcode值,做比较,可以极大程度的减少此类死锁的发生.

代码示例:在Account中加入此方法.

  //控制了锁顺序,避免死锁.
    public static void transferMoney2(final Account fromAccount, final Account toAccount) {

        try {
            if (fromAccount.hashCode() > toAccount.hashCode()) {
                synchronized (fromAccount) {
                  System.out.println(">  " + Thread.currentThread().getName() + ":" + fromAccount.hashCode());
                    Thread.sleep(10);

                    synchronized (toAccount) {
                        Thread.sleep(10);
                    }
                }
            } else if (fromAccount.hashCode() < toAccount.hashCode()) {
                synchronized (toAccount) {
                      System.out.println("<  " + Thread.currentThread().getName() + ":" + toAccount.hashCode());
                    Thread.sleep(10);

                    synchronized (fromAccount) {
                        Thread.sleep(10);
                    }
                }
            } else {
                //如果两个对象的hashcode值相等,则统一使用一个锁来减少死锁的产生.
                synchronized (lock) {
                    synchronized (fromAccount) {
                        Thread.sleep(10);

                        synchronized (toAccount) {
                            Thread.sleep(10);
                        }
                    }
                }
            }
        } catch (Exception e) {

        }
    }

在TestAccount中调用transferMoney2方法.
结果::
了解多线程中的死锁及减少死锁的方法
从结果得知:
1.我们通过对象的hashcode值做比较,在方法里面,通过控制对象的引用,控制了获取锁的顺序.
2.在极少数情况下,如果出现两个对象的hashcode值相等,我们通过定义其他的锁,来减少死锁的产生.


尝试定时的锁

尝试定时的锁是,可以使用显示的锁,lock.tryLock方法.
代码示例:

  private static final Lock lock1 = new ReentrantLock();
 public static void getLock() {
        try {
            if (lock1.tryLock()) {
                System.out.println(Thread.currentThread().getName() + ": success get lock");
                lock1.unlock();
            } else {
                System.out.println(Thread.currentThread().getName() + ": fail get lock");
            }

        } catch (Exception e) {

        }
    }

测试方法中:

 Runnable runnable = new Runnable() {
            @Override
            public void run() {
                Account.getLock();
            }
        };
        Thread threadC = new Thread(runnable, "threadC");
        Thread threadD = new Thread(runnable, "threadD");
        threadC.start();
        threadD.start();

测试结果:
了解多线程中的死锁及减少死锁的方法
结论:
1.lock.tryLock是尝试去获取锁,如果没有获得则返回false,成功获取返回ture,
2.tryLock方法可以设置时间,在一定时间内尝试获取锁.具体使用可自己测试尝试.
3.通过tryLock方法,也可减少死锁的发生.


总结:
1.一个线程在执行操作时可能调用多个方法可能获得多个锁时,尽可能拆分锁,使之每个线程在处理一个操作时,只获得一个锁.即释放当前锁,再去获得下一个锁.
2.在使用锁时,尽量减少锁的粒度.
3.减少锁的占用时间.

本文地址:https://blog.csdn.net/weixin_43225491/article/details/110240053