死锁的产生
死锁概念:并发下,线程因为相互等待对方资源,导致"永久"阻塞的现象。
死锁产生的4大必要条件
- 资源互斥:一个资源每次只能被一个线程使用
- 请求与保持条件:一个线程因请求资源而阻塞时,对方已经获得的资源保持不变
- 不剥夺条件:线程已经获得的资源,在未使用之前不能剥夺
- 循环等待条件:若干线程形成的一种首尾相接的循环等待资源关系
死锁示例
package com.DeadLock;
/**
* @author wty
* @date 2022/10/17 15:36
* <p>
* 模拟死锁过程
*/
public class DeadLock {
public static void main(String[] args) {
DeadLockExercise deadLockExercise1 = new DeadLockExercise(true);
deadLockExercise1.setName("A线程");
DeadLockExercise deadLockExercise2 = new DeadLockExercise(false);
deadLockExercise2.setName("B线程");
deadLockExercise1.start();
deadLockExercise2.start();
}
}
class DeadLockExercise extends Thread {
static Object object1 = new Object();
static Object object2 = new Object();
private boolean loop;
@Override
public void run() {
/**
* 业务逻辑分析
* 1.如果flag为T,线程A就会先得到/持有object1对象锁,然后尝试获取object2对象锁
* 2.如果线程A得不到object2对象锁,就会Blocked
* 3.如果flag为F,线程B就会先得到/持有object2对象锁
*/
if (loop) {
synchronized (object1) {
System.out.println(Thread.currentThread().getName() + " 1对象出口1");
synchronized (object2) {
System.out.println(Thread.currentThread().getName() + " 2对象出口1");
}
}
} else {
synchronized (object2) {
System.out.println(Thread.currentThread().getName() + " 2对象出口2");
synchronized (object1) {
System.out.println(Thread.currentThread().getName() + " 1对象出口2");
}
}
}
}
public DeadLockExercise(boolean loop) {
this.loop = loop;
}
}
首先在Terminal窗口,Local处
输入jps查看目前运行程序的进程
查找到死锁进程号之后,
jstack + 进程号
定位到死锁的原因是A线程上锁,等待B线程释放锁,如此循环等待形成了死锁。
解决简单死锁的方法
死锁的解决要根据产生死锁的条件来进行:
1.针对资源互斥
可以根据代码逻辑去掉互斥锁逻辑,比如涉及金额的话用AtomicInteger(原子操作)、ThreadLocal、CAS乐观锁
2.针对请求与保持条件
可以用List集合将2个资源放在一个集合中,通过contains判断,如果集合中有元素,就用一个boolean变量标记false,直到2个资源释放后,变量标记true后面的线程才能用
3.针对不可剥夺条件
通过this.lock.tryLock(2, TimeUnit.SECONDS);设置时长锁等待
class A extends Thread{
private final Lock lock = new ReentrantLock(); // 设置锁
public boolean loop = true;
public static Object object1 = new Object();
public static Object object2 = new Object();
public A(boolean loop){
this.loop = loop;
try {
this.lock.tryLock(2, TimeUnit.SECONDS);// 设置锁等待
} catch (InterruptedException e) {
e.printStackTrace();
}
}
4.针对循环等待条件
锁排序
比如账户扣钱死锁,可以给账户设置一个id,然后锁对象的时候根据id的大小顺序来锁对象。
5.银行家算法