java并发--死锁

作者:opLW

java并发–死锁

目录

1.什么是死锁
2.出现死锁的原因
3.出现死锁四个必要条件
4.处理死锁的方法
4.死锁,活锁和饥饿的区别

  • 什么是死锁:
    • 死锁是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。

  • 出现死锁的原因:
    • 因系统资源不足导致的资源竞争
    • 进程运行推进顺序不合适:请求和释放资源顺序不当
    • 资源分配不当

  • 出现死锁四个必要条件(必须同时具备):
    • 资源互斥:一个资源只能被一个进程使用
    • 请求与保持:当一个进程因请求资源而阻塞时候,保持已获得资源不放
    • 不剥夺:进程已获得资源,在未使用完成之前,不能被其他进程强行剥夺
    • 循环等待:若干进程之间形成一种头尾相接的循环等待资源关系

    例子: lock(m1) lock(m2) unlock(m1) lock(m1) unlock(m2) unlock(m1)
    假设有两个线程,线程1执行到lock(m1),lock(m2),unlock(m1),此时线程1持有锁m2,想要获取锁m1;线程2执行到lock(m1),此时线程2持有锁m1,想要获取锁m2。两个线程都拿着对方想要得到的锁,造成死锁。

  • 处理死锁的方法:

    • 死锁预防 通过设置某些限制条件,去破坏死锁产生的四个必要条件的一个或多个。易实现,被广泛应用,但由于所施加的限制条件往往太严格,因而可能导致系统资源的利用率和吞吐率降低。(资源互斥:一个资源只能被一个进程使用,这个必要条件本身就不可能被破坏,所以主要破坏的是其他三个)
    • 死锁避免 在资源的动态分配过程中,用某种方法防止系统进入不安全状态,从而避免死锁,而不需要事先采取各种限制措施去破坏产生死锁的四个必要条件。这种方法施加的限制条件较弱,但实现上有一定的困难。
    • 区别 死锁预防是设法至少破坏产生死锁的四个必要条件之一,严格的防止死锁出现,而死锁避免则不那么严格的限制产生死锁的必要条件的存在,因为即使死锁的必要条件存在,也不一定发生死锁,死锁避免是在系统运行过程中注意避免死锁的最终发生。
    • 记忆方法 死锁预防顾名思义就是提前做好工作,防范未然。所以对应破坏死锁产生的四个必要条件的一个或多个,对应一个静态的处理;而死锁避免则对应一个在发生过程中的动态避免

(1) 死锁例子:

public class Client {
   private static final String a = "a";
   private static final String b = "b";

   public static void main(String[] args) {
       Runnable r1 = new Runnable() {
           @Override
           public void run() {
               synchronized (a) { 
                   System.out.println(Thread.currentThread().getName() + "取得:" + a);
                   try{
                       Thread.sleep(2000); //确保另外一个线程已经启动
                   } catch (InterruptedException e) {
                       e.printStackTrace();
                   }
                   synchronized (b) {
                       System.out.println(Thread.currentThread().getName() + "取得:" + b);
                   }
               }
           }
       };

       Runnable r2 = new Runnable() {
           @Override
           public void run() {
               synchronized (b) {
                   System.out.println(Thread.currentThread().getName() + "取得:" + b);
                   try{
                       Thread.sleep(2000);
                   } catch (InterruptedException e) {
                       e.printStackTrace();
                   }
                   synchronized (a) {
                       System.out.println(Thread.currentThread().getName() + "取得:" + a);
                   }
               }
           }
       };

       Thread t1 = new Thread(r1);
       t1.setName("线程1");
       Thread t2 = new Thread(r2);
       t2.setName("线程2");
       t1.start();
       t2.start();
   }
}
  • synchronized (a) 时收到IDEA的提醒:Reports synchronized statements where the lock expression is a reference to a non-final field. Such statements are unlikely to have useful semantics, as different threads may be locking on different objects even when operating on the same object.(大概意思是说:锁的是一个non-final的引用,这些语句可能没有作用,因为不同的线程可能锁的是不同的对象)
  • 原因: 没有被final修饰的引用,可能会被指向别的对象,如果在多线程中发生这种情况,就会使锁失效。以上是个人理解,详情可看 https://stackoverflow.com/questions/6910807/synchronization-of-non-final-field

(2)活锁例子:

public class Client {
    static class Spoon {
        private Diner owner;
        public Spoon(Diner d) { owner = d; }
        public Diner getOwner() { return owner; }
        public synchronized void setOwner(Diner d) { owner = d; }
        public synchronized void use() {
            System.out.printf("%s has eaten!", owner.name);
        }
    }

    static class Diner {
        private String name;
        private boolean isHungry;

        public Diner(String n) { name = n; isHungry = true; }
        public String getName() { return name; }
        public boolean isHungry() { return isHungry; }

        public void eatWith(Spoon spoon, Diner spouse) {
            while (isHungry) {
                // Don't have the spoon, so wait patiently for spouse.
                if (spoon.owner != this) {
                    try { Thread.sleep(1); }
                    catch(InterruptedException e) { continue; }
                    continue;
                }

                // If spouse is hungry, insist upon passing the spoon.
                if (spouse.isHungry()) {
                    System.out.printf(
                            "%s: You eat first my darling %s!%n",
                            name, spouse.getName());
                    spoon.setOwner(spouse);
                    continue;
                }

                // Spouse wasn't hungry, so finally eat
                spoon.use();
                isHungry = false;
                System.out.printf(
                        "%s: I am stuffed, my darling %s!%n",
                        name, spouse.getName());
                spoon.setOwner(spouse);
            }
        }
    }

    public static void main(String[] args) {
        final Diner husband = new Diner("Bob");
        final Diner wife = new Diner("Alice");

        final Spoon s = new Spoon(husband);

        new Thread(new Runnable() {
            public void run() { husband.eatWith(s, wife); }
        }).start();

        new Thread(new Runnable() {
            public void run() { wife.eatWith(s, husband); }
        }).start();
    }
}

万水千山总是情,麻烦手下别留情。
如若讲得有不妥,文末留言告知我,
如若觉得还可以,收藏点赞要一起。

opLW原创七言律诗,转载请注明出处

扫描二维码关注公众号,回复: 8508951 查看本文章
发布了21 篇原创文章 · 获赞 28 · 访问量 7332

猜你喜欢

转载自blog.csdn.net/qq_36518248/article/details/89030098
今日推荐