死锁
什么是死锁?
简单举个例子,前提:两个线程共享的对象o1和o2,线程A和线程B。
如果线程A先锁住o1然后锁住o2,线程B先锁住o2然后锁住o1,
但是问题关键是:如果线程A锁住了o1,线程B同时锁住了o2;
那么会导致线程A没办法锁住o2,线程B没办法锁住o1,那么线程A、B互相等待对象释放锁,而发生了死锁。
死锁的必要条件
- 互斥条件(简单来说是有锁的存在)
- 请求和保持条件(线程已经占用了一个资源,同时也请求另一个资源,但是请求的这个资源已经被别的线程占用了)
- 不剥夺条件(指线程占用的资源,只会在线程执行完自己释放)
- 环路等待条件(多个线程之间存在循环等待)
实例代码
package com.mark.example.deadLock;
import lombok.extern.slf4j.Slf4j;
/**
* author:Mark
* date:2018/8/7 22:16
* 运行结果:死锁
* flag ==1,demo1启动的 线程1 进入该代码块,先是申请并锁住object1,睡眠5s后再申请锁住object2
* flag ==2,demo2启动的 线程2 进入该代码块,先是申请并锁住object2,睡眠5s后再申请锁住object1
*
* 线程1睡眠结束后才能锁定object2,然后代码继续执行,但是此时object2已经被线程2锁住了
* 同样:线程2睡眠结束后才能锁定object1,然后代码继续执行,但是此时object1已经被线程1锁住了
*
* 这样就导致了线程1,线程2互相等待,都需要得到对方锁定的资源才能执行往下执行,从而死锁。
*/
@Slf4j
public class DeadLockDemo implements Runnable{
public int flag = 1;//标记
//声明两个线程共享的静态变量object1,object2
private static Object object1 = new Object();
private static Object object2 = new Object();
@Override
public void run() {
log.info("flag is {}", flag);
//demo1启动的线程进入该代码块,先是申请并锁住object1,睡眠5s后再申请锁住object2
if (flag == 1) {
synchronized (object1){
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (object2) {
log.info("锁住对象object2");
}
}
}
//demo2启动的线程进入该代码块,先是申请并锁住object2,睡眠5s后再申请锁住object1
if (flag == 0) {
synchronized (object2){
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (object1) {
log.info("锁住对象object1");
}
}
}
}
public static void main(String[] args) {
DeadLockDemo demo1 = new DeadLockDemo();
DeadLockDemo demo2 = new DeadLockDemo();
demo1.flag = 1;
demo2.flag = 0;
//启动两个线程执行,注意JVM先调度那个线程是不确定的
new Thread(demo1).start();
new Thread(demo2).start();
}
}
死锁的防止
- 破除互斥条件(一般无法破除)
- 破除请求和保持条件(一次性获取资源)
- 破除不剥夺条件(设置加锁时限)
- 破除环路等待条件(按照顺序获取资源,也就是加锁的顺序设计合理 )