死锁说白了,就是在程序中出现了锁的嵌套。
死锁不是一个知识点,它是一个错误。
现在我们学习死锁的目的就是:以后不要犯这个错误。
A线程拿着A锁,B线程拿着B锁,它们都在等着对方释放锁,此时程序就会卡死,运行不下去。
由于死锁不是一个知识点,而是一个错误,因此下面的代码是不需要你去练的,你只需要理解它的过程就行了
测试类
yThread t1 = new MyThread();
MyThread t2 = new MyThread();
t1.setName("线程A");
t2.setName("线程B");
t1.start();
t2.start();
MyThread.java
public class MyThread extends Thread {
//定义了两把锁,一把A锁一把B锁
static Object objA = new Object();
static Object objB = new Object();
@Override
public void run() {
//1.循环
while (true) {
//如果是线程A就执行这段
if ("线程A".equals(getName())) {
//先是A锁
synchronized (objA) {
System.out.println("线程A拿到了A锁,准备拿B锁");//A
//再是B锁
synchronized (objB) {
System.out.println("线程A拿到了B锁,顺利执行完一轮");
}
}
} else if ("线程B".equals(getName())) { //如果是线程B,就执行这段
if ("线程B".equals(getName())) {
//但是在线程B中,先是B锁
synchronized (objB) {
System.out.println("线程B拿到了B锁,准备拿A锁");//B
//再是A锁
synchronized (objA) {
System.out.println("线程B拿到了A锁,顺利执行完一轮");
}
}
}
}
}
}
}
此时它会出现什么情况呢?运行程序发现,程序卡死了。
那为什么会开始呢?
首先还是线程A、线程B在抢夺CPU的执行权
@Override
public void run() { //A //B
....
}
假设现在是线程A抢到了,此时它就会进去,然后做一个判断,是线程A就会执行线程A里面的代码
在执行的时候默认情况下锁都是打开的,所以A可以拿到 objA
,即A锁
@Override
public void run() {
while (true) {
if ("线程A".equals(getName())) {
synchronized (objA) { //拿到A锁
//一旦拿到后,A锁就关闭了
System.out.println("线程A拿到了A锁,准备拿B锁");//然后打印这句话
//但是当线程A还没有拿到B锁,此时CPU的执行权被上面的线程B抢到了
synchronized (objB) {
System.out.println("线程A拿到了B锁,顺利执行完一轮");
}
}
} else if ("线程B".equals(getName())) {
//线程B就会执行到这段代码,此时B锁是打开的,因为线程A停在了第7行,它导致了A锁关闭,但是它还没去操作B锁
synchronized (objB) { //因此此时线程B在执行的时候,看到B锁,是可以进来的
System.out.println("线程B拿到了B锁,准备拿A锁");//进来后就会打印这句话
//但是此时就出问题了,B如果想要继续往下执行,就必须要让A锁打开
//但是A锁打开不了,因为线程A还在拿着A锁
//那A锁什么时候才能释放呢?很简单,只有当synchronized (objA) 代码块中所有代码都执行完了,A锁才能释放。
//说白了线程A需要拿到B锁然后执行完里面的代码A锁才能释放
//但是线程A进不去,因为B锁已经被线程B拿到了
synchronized (objA) {
System.out.println("线程B拿到了A锁,顺利执行完一轮");
}
}
}
}
}
此时线程A正在等着线程B释放锁,而线程B又在等着线程A释放锁,此时两个线程都会卡死在对应的地方:A线程卡死在第7行,B线程卡死在16行,这个时候程序就运行不下去了,这个就是死锁
总结:以后我们在写锁的时候,千万不要让两个锁嵌套就行了。