一、原因:
线程间互相等待资源,但是又不释放自己自身的资源,导致无穷无尽的等待,其结果是系统任务永远无法执行完成。例如过独木桥,A和B分别走在桥上,想通过独木桥,但是他们都希望对方先退出,两个人就一直处在等待状态。
二、产生条件:
1、互斥条件:一个资源每次只能被一个进程使用。独木桥每次只能通过一个人。
2、请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。乙不退出桥面,甲也不退出桥面。
3、不剥夺条件: 进程已获得的资源,在未使用完之前,不能强行剥夺。甲不能强制乙退出桥面,乙也不能强制甲退出桥面。
4、循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。如果乙不退出桥面,甲不能通过,甲不退出桥面,乙不能通过。
三、案例
public class DeadlockTest {
public static void main(String[] args) {
String str1 = new String("资源1");
String str2 = new String("资源2");
new Thread(new Lock(str1, str2), "线程1").start();
new Thread(new Lock(str2, str1), "线程2").start();
}
}
class Lock implements Runnable {
private String str1;
private String str2;
public Lock(String str1, String str2) {
super();
this.str1 = str1;
this.str2 = str2;
}
@Override
public void run() {
try {
System.out.println(Thread.currentThread().getName() + "运行");
synchronized (str1) {
System.out.println(Thread.currentThread().getName() + "锁住"
+ str1);
Thread.sleep(1000);
synchronized (str2) {
// 执行不到这里
System.out.println(Thread.currentThread().getName()
+ "锁住-----" + str2);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
最后执行的结果是
线程1运行
线程2运行
线程2锁住资源2
线程1锁住资源1
第一个线程锁住了资源1(甲占有桥的一部分资源),第二个线程锁住了资源2(乙占有桥的一部分资源),线程1企图锁住资源2(甲让乙退出桥面,乙不从),进入阻塞,线程2企图锁住资源1(乙让甲退出桥面,甲不从),进入阻塞,死锁了。
四、解决方案:
1、锁的顺序,让两个线程获取锁的顺序是一直,则不会出现死锁
public class Demo2 {
public static void main(String[] args) {
Object bigGate = new Object();
Object smallGate = new Object();
new Thread(new Runnable() {
@Override
public void run() {
String name = Thread.currentThread().getName();
synchronized (bigGate){
System.out.println(name + ":我把大门给锁了...然后我休息一下...");
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(name + ":我现在要进入小门.....");
synchronized (smallGate){
System.out.println(name + ":我终于进来了.....");
}
}
}
},"小明").start();
new Thread(new Runnable() {
@Override
public void run() {
String name = Thread.currentThread().getName();
synchronized (bigGate){
System.out.println(name + ":我把大门给锁了...然后我休息一下...");
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(name + ":我现在要进入小门.....");
synchronized (smallGate){
System.out.println(name + ":我终于进来了.....");
}
}
}
},"小红").start();
}
}
小明:我把大门给锁了...然后我休息一下...
小明:我现在要进入小门.....
小明:我终于进来了.....
小红:我把大门给锁了...然后我休息一下...
小红:我现在要进入小门.....
小红:我终于进来了.....
2、在获取锁的时候加超时时间,这里我们用之前学的Lock
来做例子
public class Demo3 {
public static void main(String[] args) {
Lock bigGate = new ReentrantLock();
Lock smallGate = new ReentrantLock();
Random random = new Random();
new Thread(new Runnable() {
@Override
public void run() {
String name = Thread.currentThread().getName();
bigGate.lock();
try {
System.out.println(name + ":我把大门给锁了...然后我休息一下...");
Thread.sleep(100);
System.out.println(name + ":我现在要进入小门.....");
if(smallGate.tryLock(random.nextInt(500), TimeUnit.MILLISECONDS)){
try {
System.out.println(name + ":我终于进来了.....");
}finally {
smallGate.unlock();
}
}else{
System.out.println(name + ":我进不去小门,算了,不进了...");
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
bigGate.unlock();
}
}
},"小明").start();
new Thread(new Runnable() {
@Override
public void run() {
String name = Thread.currentThread().getName();
smallGate.lock();
try {
System.out.println(name + ":我把小门给锁了...然后我休息一下...");
Thread.sleep(100);
System.out.println(name + ":我现在要进入大门.....");
if(bigGate.tryLock(random.nextInt(500), TimeUnit.MILLISECONDS)){
try {
System.out.println(name + ":我终于进来了.....");
}finally {
bigGate.unlock();
}
}else{
System.out.println(name + ":我进不去大门,算了,不进了...");
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
smallGate.unlock();
}
}
},"小红").start();
}
}
小明:我把大门给锁了...然后我休息一下...
小红:我把小门给锁了...然后我休息一下...
小明:我现在要进入小门.....
小红:我现在要进入大门.....
小红:我进不去大门,算了,不进了...
小明:我终于进来了.....