Java死锁

来源:互联网 发布:网络pos支付平台 编辑:程序博客网 时间:2024/06/05 15:24

什么是死锁

死锁:多个线程同时被阻塞,它们中的一个或者全部都在等待某个资源被释放。由于线程被无限期的阻塞,因此程序不可能正常终止。(两个甚至多个线程被永久阻塞时的一种运行局面,这种局面的生成伴随着至少两个线程和两个或者多个资源。)

死锁的产生条件

  1. 互斥使用:即当资源被一个线程占用(使用)时,别的线程不能使用。
  2. 不可抢占:资源请求者不能强制从资源占有者手中抢夺资源,资源只能等待占有者主动释放。
  3. 请求和保持:即当资源请求者在请求其他资源的同时保持对原有资源的占有。
  4. 循环等待:存在一个等待队列,P1占有p2的资源,p2占有p3的资源,p3占有p1的资源。就这样形成了一个等待环路。

打个比方用来帮助理解:
程序猿:cxy1 , 程序猿:cxy2 , 程序猿:cxy3
程序媛:cxyX , 程序媛:cxyY , 程序媛:cxyZ
1. 互斥使用:当程序猿占用一个程序媛时,别的程序猿不能再去占用这个程序媛。
2. 不可抢占:不能挖墙脚。
3. 请求和保持:当一个程序猿在占用一个程序媛时,还可以向另一个程序媛表白(请求)。
4. 循环等待:cxy1喜欢cxyX却占用cxy2喜欢的cxyY,cxy2占用着cxy3喜欢的cxyZ,cxy3占用着cxy1喜欢的cxyX,他们都在等待对方放手。

当上述四个条件都成立时,则形成死锁。当然,死锁的情况下如果打破上述任何一个条件,便可让死锁消失。

死锁的Java实例:

public class DeadLock {    public static String obj1 = "obj1";    public static String obj2 = "obj2";    public static void main(String[] args) {        LockA lockA = new LockA();        new Thread(lockA).start();        LockB lockB = new LockB();        new Thread(lockB).start();    }    static class LockA implements Runnable {        public void run() {            try {                System.out.println(new Date().toString() + " : LockA 开始执行");                while (true) {                    synchronized (obj1) {                        System.out.println(new Date().toString() + " : LockA 锁住了obj1");                        Thread.sleep(3000);// 获取obj1后先等一会儿,让LockA有足够的时间锁住obj2                        synchronized (obj2) {                            System.out.println(new Date().toString() + " : LockA 锁住了obj2");                            Thread.sleep(60 * 1000);// 为测试,占用了就不放                        }                    }                }            } catch (InterruptedException e) {                e.printStackTrace();            }        }    }    static class LockB implements Runnable {        public void run() {            try {                System.out.println(new Date().toString() + " : LockB 开始执行");                while (true) {                    synchronized (obj2) {                        System.out.println(new Date().toString() + " : LockB 锁住了obj2");                        Thread.sleep(3000);// 获取obj2后先等一会儿,让LockB有足够的时间锁住obj1                        synchronized (obj1) {                            System.out.println(new Date().toString() + " : LockB 锁住了obj1");                            Thread.sleep(60 * 1000);// 为测试,占用了就不放                        }                    }                }            } catch (InterruptedException e) {                e.printStackTrace();            }        }    }}//结果打印Wed Nov 01 17:38:00 CST 2017 : LockB 开始执行Wed Nov 01 17:38:00 CST 2017 : LockB 锁住了obj2Wed Nov 01 17:38:00 CST 2017 : LockA 开始执行Wed Nov 01 17:38:00 CST 2017 : LockA 锁住了obj1

信号量(解决死锁)

为了解决这个问题,我们不使用显示的去锁,我们用信号量去控制。
信号量可以控制资源能被多少线程访问,这里我们指定只能被一个线程访问,就做到了类似锁住。而信号量可以指定去获取的超时时间,我们可以根据这个超时时间,去做一个额外处理。
对于无法成功获取的情况,一般就是重复尝试,或指定尝试的次数,也可以马上退出。
详细解决方案参考:Java死锁和解决方式

原创粉丝点击