1.死锁是指两个或多个线程互相等待对方释放所持有的资源,从而导致进程无法继续执行的一种情况。具体来说,死锁发生时线程会进入一个永久等待的状态,无法继续执行并最终导致程序无响应或崩溃。
2.产生死锁的条件,通常被称为死锁的四个必要条件,包括:
- 互斥条件(Mutual Exclusion):至少有一个资源被多个线程独占,也就是说一个资源同时只能被一个线程占用。
- 请求与保持条件(Hold and Wait):线程已经持有了至少一个资源,并且在等待获取其他线程占有的资源。
- 不可剥夺条件(No Preemption):已经获得的资源不能被其他线程抢占,只能由占有它的线程显示地释放。
- 循环等待条件(Circular Wait):多个线程形成一个循环等待的等待链,即每个线程都在等待下一个线程所持有的资源。
只有当上述四个条件同时满足时,死锁才可能发生。
public static void main(String[] args) {
final Object resource1 = new Object();
final Object resource2 = new Object();
Thread thread1 = new Thread(() -> {
synchronized (resource1) {
System.out.println("Thread 1:锁住offer1");
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (resource2) {
System.out.println("Thread 1:锁住offer2");
}
});
Thread thread2 = new Thread(() -> {
synchronized (resource2) {
System.out.println("Thread 2:锁住offer2");
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (resource1) {
System.out.println("Thread 2:锁住offer1");
}
});
thread1.start();
thread2.start();
}
public static void main(String[] args) {
final Object resource1 = new Object();
final Object resource2 = new Object();
Thread thread1 = new Thread(() -> {
synchronized (resource1) {
System.out.println("半生:锁住了机考第一名");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (resource2) {
System.out.println("半生:笔试第一名");
}
}
});
Thread thread2 = new Thread(() -> {
synchronized (resource2) {
System.out.println("小一:锁住了笔试第一名");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (resource1) {
System.out.println("小一:锁住机考第一名");
}
}
});
thread1.start();
thread2.start();
}
又是你俩,干啥呢,死锁了呀~offer不止一个呀,就不能友好相处,快乐的玩耍同时都拿到offer么
1. 先使用javac -encoding UTF-8 X.java, 来生成class文件
2. javap -verbose X.class 反编译
3.从反编译的指令来看,这里应该是操作系统或者jvm虚拟机检查到了这是个死锁,强制中断了,在使用synchronized作为锁的时候,我们知道是有monitorenter monitorexit 这一对指令的,但是这里就没有看到
4. 下载了个idea的插件jclasslib
来查看,看字节码指令是否一致
5.可以看到这里是有这一对锁指令的,这里稍微解释下上写字节码指令的含义
0 aload_0:加载索引为0的引用到操作数栈,通常用于加载实例方法的隐式参数,即this。
1 dup:复制栈顶的元素,并将复制后的值重新压入栈顶。
2 astore_2:将栈顶的引用类型数值存储到局部变量表的索引为2的位置。
3 monitorenter:进入同步块前获取锁。
4 getstatic #3 <java/lang/System.out : Ljava/io/PrintStream;>:获取静态字段System.out的值,即标准输出流PrintStream对象。
7 ldc #12 <小一:锁住了笔试第一名>:将常量池中索引为12的String类型常量加载到操作数栈。
9 invokevirtual #5 <java/io/PrintStream.println : (Ljava/lang/String;)V>:执行PrintStream对象的println方法,其中参数为栈顶的String类型常量。
12 ldc2_w #13 <1000>:将常量池中索引为13的long类型常量加载到操作数栈。
15 invokestatic #15 <java/lang/Thread.sleep : (J)V>:执行Thread类的静态方法sleep,其中参数为栈顶的long类型常量。
18 goto 33 (+15):无条件跳转到字节码指令33,即跳过下方的指令。
21 astore_3:将栈顶的引用类型数值存储到局部变量表的索引为3的位置。
22 getstatic #3 <java/lang/System.out : Ljava/io/PrintStream;>:获取静态字段System.out的值。
25 ldc #17 <小一:被中断,释放笔试资源>:将常量池中索引为17的String类型常量加载到操作数栈。
27 invokevirtual #5 <java/io/PrintStream.println : (Ljava/lang/String;)V>:执行PrintStream对象的println方法,其中参数为栈顶的String类型常量。
30 aload_2:加载局部变量表中索引为2的引用类型数值到操作数栈。
31 monitorexit:退出同步块,释放锁。
32 return:返回void类型的值,并结束当前方法。
33 aload_1:加载局部变量表中索引为1的引用类型数值到操作数栈。
34 dup:复制栈顶的元素,并将复制后的值重新压入栈顶。
35 astore_3:将栈顶的引用类型数值存储到局部变量表的索引为3的位置。
这里的指令
第9 invokevirtual #5 <java/io/PrintStream.println : (Ljava/lang/String;)V>:执行PrintStream对象的println方法,其中参数为栈顶的String类型常量。执行了catch中的打印日志,说明被执行中断了,后面goto 33 跳到33行的指令
6.此外我另外写了同步方法,来看看
从这里看出,它是有加锁的,第20行多了一个monitorexit,这就是防止异常强制释放锁,也就是synchronized能自动释放锁的保障
退一步想阔天空,你好我好大家好
public static void main(String[] args) {
final Object resource1 = new Object();
final Object resource2 = new Object();
Thread thread1 = new Thread(() -> {
synchronized (resource1) {
System.out.println("半生:获得了机考第一名");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (resource2) {
System.out.println("半生:获得了笔试第一名");
}
}
});
Thread thread2 = new Thread(() -> {
synchronized (resource1) {
System.out.println("小一:获得了笔试第一名");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (resource2) {
System.out.println("小一:获得了机考第一名");
}
}
});
thread1.start();
thread2.start();
}
只要稍微改下获取资源的顺序,半生跟小一就分别都获取了机考,笔试第一名,都收到了XX大厂offer
打破死锁的方式有多种,只要四个死锁的必要条件去其一就可以了