了解多线程中的死锁及减少死锁的方法
了解死锁
死锁是如何产生的?
介绍:
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
上一篇: 正则匹配的test函数