27 死锁

死锁

多个线程各自占有一些共享资源,并且互相等待其他线程占有的资源才能运行,而导致两个或者多个线程都在等待对方释放资源,都停止执行的情形,某一个同步块,同时拥有“两个以上对象的锁”时,就可能发生“死锁”问题

 

//死锁  多个线程互相抱着对象需要的资源,形成僵持
public class TestLock {

   public static void main(String[] args) {

       Makeup g1 = new Makeup(0,"灰姑凉");
       Makeup g2 = new Makeup(0,"白雪");

       g1.start();
       g2.start();
  }

}

//口红
class Lipstick{

}

//镜子
class Mirror{

}


//化妆
class Makeup extends Thread{

   //需要的资源只有一份,用static保证只有一份
   static Lipstick lipstick = new Lipstick();
   static Mirror mirror = new Mirror();

   int choice;
   String girlName;//使用化妆品的人

   Makeup(int choice,String girlName){
       this.choice = choice;
       this.girlName = girlName;

  }

   @Override
   public void run() {
       //化妆
       try {
           makeup();
      } catch (InterruptedException e) {
           e.printStackTrace();
      }
  }

   //互相持有对方的锁,就是需要拿到对方的资源
   private void makeup() throws InterruptedException {
       if(choice == 0){
           synchronized (lipstick){//获得口红的锁
               System.out.println(this.girlName+"获得口红的锁 ");
               Thread.sleep(1000);
          }
           synchronized (mirror) {//一秒钟后获得了镜子
               System.out.println(this.girlName + "获得镜子的锁");
          }
      }else {
           synchronized (mirror) {//获得镜子的锁
               System.out.println(this.girlName + "获得镜子的锁");
               Thread.sleep(1000);
          }
           synchronized (lipstick) {//一秒钟后获得了口红
               System.out.println(this.girlName + "获得口红的锁");
          }
      }
  }

}

 

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

  1. 互斥条件:一个资源只能被一个进程使用

  2. 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放

  3. 不剥夺条件:进程已获得的资源,在未使用完之前,不能强行剥夺

  4. 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系

 

lock(锁)

显式定义同步锁 同步锁使用lock对象充当

java.util.concurrent.locks.lock接口是控制多个线程对共享资源进行访问的工具。线提供合理对共享资源的独占访问,每次只能有一个线程对Lock对象加锁,线程开始访问共享资源前应先获得Lock对象

ReentrantLock(可重入锁)类实现了Lock,它拥有与synchronized相同的并发现和内存语义,在实现线程安全的控制中,比较常用的是ReentrantLock,可以显示加锁、释放锁

//测试lock锁
public class TestLock {
   public static void main(String[] args) {
       TestLock2 testLock2 = new TestLock2();

       new Thread(testLock2).start();
       new Thread(testLock2).start();
       new Thread(testLock2).start();

  }
}


class TestLock2 implements Runnable{

    int ticketNums = 10;

    //定义lock锁
 private final  ReentrantLock lock = new ReentrantLock();

   @Override
   public void run() {
       while(true){

           try{
               lock.lock();//加锁 需要try catch
               if (ticketNums > 0){
                   try {
                       Thread.sleep(1000);
                  } catch (InterruptedException e) {
                       e.printStackTrace();
                  }
                   System.out.println(ticketNums--);
              }else{
                   break;
              }
          }finally {
               //解锁
               lock.unlock();
          }

      }
  }
}

 

Synchronized 与Lock 区别

  • Lock是显示锁(手动开启和关闭锁,别忘记关闭锁)synchronized是隐式锁,出了作用域自动释放

  • Lock只有代码块锁,synchronized有代码锁和方法锁

  • 使用Lock锁,jvm将花费较少的时间来调度线程,性能更好。并且具有更好的扩展性(提供更多的子类)

  • 优先使用顺序

    Lock > 同步代码块(已经进入了方法体,分配了相应的资源)>同步方法(在方法体之外)

  •  

 

 

posted @   flypiggg  阅读(169)  评论(0编辑  收藏  举报
编辑推荐:
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
阅读排行:
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)
点击右上角即可分享
微信分享提示