java开发中遇见死锁的解决方法

宜家博客
宜家博客
宜家博客
40428
文章
0
评论
2020年2月11日05:55:47 评论 47 3868字阅读12分53秒

这篇文章主要介绍了java 多线程死锁详解及简单实例的相关资料,需要的朋友可以参考下

死锁是这样一种情形:多个线程同时被阻塞,它们中的一个或者全部都在等待某个资源被释放。由于线程被无限期地阻塞,因此程序不可能正常终止。

java 死锁产生的四个必要条件:

1>互斥使用,即当资源被一个线程使用(占有)时,别的线程不能使用

2>不可抢占,资源请求者不能强制从资源占有者手中夺取资源,资源只能由资源占有者主动释放。

3>请求和保持,即当资源请求者在请求其他的资源的同时保持对原有资源的战友。

4>循环等待,即存在一个等待队列:P1占有P2的资源,P2占有P3的资源,P3占有P1的资源。这样就形成了一个等待环路。

当上述四个条件都成立的时候,便形成死锁。当然,死锁的情况下如果打破上述任何一个条件,便可让死锁消失。下面用java代码来模拟一下死锁的产生。

解决死锁问题的方法是:一种是用synchronized,一种是用Lock显式锁实现。

而如果不恰当的使用了锁,且出现同时要锁多个对象时,会出现死锁情况,如下:

/*
 author by w3cschool.cc
 LockTest.java
 */
import java.util.Date;
public class LockTest {
  public static String obj1 = "obj1";
  public static String obj2 = "obj2";
  public static void main(String[] args) {
   LockA la = new LockA();
   new Thread(la).start();
   LockB lb = new LockB();
   new Thread(lb).start();
  }
}
class LockA implements Runnable{
  public void run() {
   try {
     System.out.println(new Date().toString() + " LockA 开始执行");
     while(true){
      synchronized (LockTest.obj1) {
        System.out.println(new Date().toString() + " LockA 锁住 obj1");
        Thread.sleep(3000); // 此处等待是给B能锁住机会
        synchronized (LockTest.obj2) {
         System.out.println(new Date().toString() + " LockA 锁住 obj2");
         Thread.sleep(60 * 1000); // 为测试,占用了就不放
        }
      }
     }
   } catch (Exception e) {
     e.printStackTrace();
   }
  }
}
class LockB implements Runnable{
  public void run() {
   try {
     System.out.println(new Date().toString() + " LockB 开始执行");
     while(true){
      synchronized (LockTest.obj2) {
        System.out.println(new Date().toString() + " LockB 锁住 obj2");
        Thread.sleep(3000); // 此处等待是给A能锁住机会
        synchronized (LockTest.obj1) {
         System.out.println(new Date().toString() + " LockB 锁住 obj1");
         Thread.sleep(60 * 1000); // 为测试,占用了就不放
        }
      }
     }
   } catch (Exception e) {
     e.printStackTrace();
   }
  }
}

以上代码运行输出结果为:

Tue May 05 10:51:06 CST 2015 LockB 开始执行
Tue May 05 10:51:06 CST 2015 LockA 开始执行
Tue May 05 10:51:06 CST 2015 LockB 锁住 obj2
Tue May 05 10:51:06 CST 2015 LockA 锁住 obj1

此时死锁产生。

为了解决这个问题,我们不使用显示的去锁,我们用信号量去控制。

信号量可以控制资源能被多少线程访问,这里我们指定只能被一个线程访问,就做到了类似锁住。而信号量可以指定去获取的超时时间,我们可以根据这个超时时间,去做一个额外处理。

对于无法成功获取的情况,一般就是重复尝试,或指定尝试的次数,也可以马上退出。

来看下如下代码:

/*
 author by w3cschool.cc
 UnLockTest.java
 */
import java.util.Date;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
public class UnLockTest {
  public static String obj1 = "obj1";
  public static final Semaphore a1 = new Semaphore(1);
  public static String obj2 = "obj2";
  public static final Semaphore a2 = new Semaphore(1);
  public static void main(String[] args) {
   LockAa la = new LockAa();
   new Thread(la).start();
   LockBb lb = new LockBb();
   new Thread(lb).start();
  }
}
class LockAa implements Runnable {
  public void run() {
   try {
     System.out.println(new Date().toString() + " LockA 开始执行");
     while (true) {
      if (UnLockTest.a1.tryAcquire(1, TimeUnit.SECONDS)) {
        System.out.println(new Date().toString() + " LockA 锁住 obj1");
        if (UnLockTest.a2.tryAcquire(1, TimeUnit.SECONDS)) {
         System.out.println(new Date().toString() + " LockA 锁住 obj2");
         Thread.sleep(60 * 1000); // do something
        }else{
         System.out.println(new Date().toString() + "LockA 锁 obj2 失败");
        }
      }else{
        System.out.println(new Date().toString() + "LockA 锁 obj1 失败");
      }
      UnLockTest.a1.release(); // 释放
      UnLockTest.a2.release();
      Thread.sleep(1000); // 马上进行尝试,现实情况下do something是不确定的
     }
   } catch (Exception e) {
     e.printStackTrace();
   }
  }
}
class LockBb implements Runnable {
  public void run() {
   try {
     System.out.println(new Date().toString() + " LockB 开始执行");
     while (true) {
      if (UnLockTest.a2.tryAcquire(1, TimeUnit.SECONDS)) {
        System.out.println(new Date().toString() + " LockB 锁住 obj2");
        if (UnLockTest.a1.tryAcquire(1, TimeUnit.SECONDS)) {
         System.out.println(new Date().toString() + " LockB 锁住 obj1");
         Thread.sleep(60 * 1000); // do something
        }else{
         System.out.println(new Date().toString() + "LockB 锁 obj1 失败");
        }
      }else{
        System.out.println(new Date().toString() + "LockB 锁 obj2 失败");
      }
      UnLockTest.a1.release(); // 释放
      UnLockTest.a2.release();
      Thread.sleep(10 * 1000); // 这里只是为了演示,所以tryAcquire只用1秒,而且B要给A让出能执行的时间,否则两个永远是死锁
     }
   } catch (Exception e) {
     e.printStackTrace();
   }
  }
}

以上实例代码输出结构为:

Tue May 05 10:59:13 CST 2015 LockA 开始执行
Tue May 05 10:59:13 CST 2015 LockB 开始执行
Tue May 05 10:59:13 CST 2015 LockB 锁住 obj2
Tue May 05 10:59:13 CST 2015 LockA 锁住 obj1
Tue May 05 10:59:14 CST 2015LockB 锁 obj1 失败
Tue May 05 10:59:14 CST 2015LockA 锁 obj2 失败
Tue May 05 10:59:15 CST 2015 LockA 锁住 obj1
Tue May 05 10:59:15 CST 2015 LockA 锁住 obj2

【相关推荐】

1. Java视频教程

2. JAVA教程手册

3. 极客学院Java视频教程

以上就是java开发中遇见死锁的解决方法的详细内容,更多请关注php教程其它相关文章!

继续阅读
weinxin
欢迎加入中国站长博客之家
本站的所有资源都会上传分享到博客之家,希望大家互相学习交流进步。
宜家博客
重拾java基础(十四):数组总结 java教程

重拾java基础(十四):数组总结

重拾java基础(十四):数组总结 一、数组的排序 数组排序的核心是:对数组的遍历。 1、冒泡排序 A、冒泡排序一共需要排序arr.length-1次;具体需要多少轮不确定 这个也需要 循环 按最多的...
Java中的继承初始化顺序 java教程

Java中的继承初始化顺序

一、继承的初始化顺序 1.初始化父类,再初始化子类。 2.先执行初始化对象中的属性,再执行构造方法中的初始化。 二、下面,我们用一个简单的例子来说明这一问题: 父类:Animal代码: package...
Java中的Object、T(泛型)、?它们的区别 java教程

Java中的Object、T(泛型)、?它们的区别

下面小编就为大家带来一篇详谈Java中的Object、T(泛型)、?区别。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧 因为最近重新看了泛型,又看了些反射,导致我对Ob...
匿名

发表评论

匿名网友 填写信息

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: