服务端开发实战java面试

死锁产生的原因和解锁的方法

2018-09-05  本文已影响13人  AKyS佐毅

死锁产生的原因和解锁的方法

举例说明

/**
 * 一个简单的死锁类
 * 当DeadLock类的对象flag==1时(td1),先锁定o1,睡眠500毫秒
 * 而td1在睡眠的时候另一个flag==0的对象(td2)线程启动,先锁定o2,睡眠500毫秒
 * td1睡眠结束后需要锁定o2才能继续执行,而此时o2已被td2锁定;
 * td2睡眠结束后需要锁定o1才能继续执行,而此时o1已被td1锁定;
 * td1、td2相互等待,都需要得到对方锁定的资源才能继续执行,从而死锁。
 */

@Slf4j
public class DeadLock implements Runnable {
    public int flag = 1;
    //静态对象是类的所有对象共享的
    private static Object o1 = new Object(), o2 = new Object();

    @Override
    public void run() {
        log.info("flag:{}", flag);
        if (flag == 1) {
            synchronized (o1) {
                try {
                    Thread.sleep(500);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                synchronized (o2) {
                    log.info("1");
                }
            }
        }
        if (flag == 0) {
            synchronized (o2) {
                try {
                    Thread.sleep(500);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                synchronized (o1) {
                    log.info("0");
                }
            }
        }
    }

    public static void main(String[] args) {
        DeadLock td1 = new DeadLock();
        DeadLock td2 = new DeadLock();
        td1.flag = 1;
        td2.flag = 0;
        //td1,td2都处于可执行状态,但JVM线程调度先执行哪个线程是不确定的。
        //td2的run()可能在td1的run()之前运行
        new Thread(td1).start();
        new Thread(td2).start();
    }
}

常利用资源分配图、进程等待图来协助这种检测。

 - 1、死锁预防(deadlock prevention)是在系统运行之前,事先考虑防止死锁发生的对策,即在最初设计各种资源调度算法时,就没法防止在系统运行过程中可能产生的死锁。Coffmman 等人[1971]曾提出进程在利用可重用性资源时产生死锁的四个必要条件:

    - 1、 **互斥使用(mutual exclusion)**:系统中存在一次只能给一个进程使用的资源。

    - 2、**占用并等待(resource holding and waiting)**:系统中存在这样的进程,它(们)已占有部分资源并等待得到另外的资源,而这些资源又被其它进程所占用还未释放。

    - 3、**非抢占分配(nonpreemption)**:资源在占有它的进程自愿交出之前,不可被其它进程所强行占用。

    - 4、**部分地分配(partial allocation)或循环等待(circular waiting)**:存在一个两个或都个进程的循环链,链中每一个进程等待被链中下一个进程占有的资源。即在一定条件下,若干进程进入了相互无休止地等待所需资源的状态。

所有这四个必要条件都应该在死锁时出现。

 - 1、每个进程必须一次性地请求它所需要的所有资源。若系统无法满足这一要求,则它不能执行。这是一种预分资源方法,它破坏了产生死锁的第三个必要条件。
 - 2、一个已占有资源的进程若要再申请新资源,它必须先释放已占资源。若随后它还需要它们,则需要重新提出申请。换言之,一个进程在使用某资源过程中可以放弃该资源,从而破坏了产生死锁的第二个必要条件。
 - 3、将系统中所有资源顺序编号,规定进程只能依次申请资源,这就是说,一个进程只有在前面的申请满足后,才能提出对其后面序号的资源的请求。这是一种有序使用资源法,它破坏了产生死锁的第四个必要条件。

2、对象锁和类锁是否会互相影响?

3、线程同步机制

线程同步是为了确保线程安全,所谓线程安全指的是多个线程对同一资源进行访问时,有可能产生数据不一致问题,导致线程访问的资源并不是安全的。如果多线程程序运行结果和单线程运行的结果是一样的,且相关变量的值与预期值一样,则是线程安全的。

Java中与线程同步有关的关键字/类包括:

volatile、synchronized、Lock、AtomicInteger等concurrent包下的原子类。。。等

volatile一般用在多个线程访问同一个变量时,对该变量进行唯一性约束,volatile保证了变量的可见性,不能保证原子性。

用法(例):

private volatile booleanflag = false

AtomicInteger atomicInteger = new AtomicInteger();  
atomicInteger.getAndIncrement();//实现原子自增  
atomicInteger.getAndDecrement();//实现原子自减  
Synchronized(this){  
    value++;  
}  
private Lock lock = new ReentrantLock();  
 private final int incrementAndGet(){  
     lock.lock();  
     try  {  
         return value++;  
     }  finally  {  
         // TODO: handle finally clause  
         lock.unlock();  
     }  
 }  

4、如何保证多线程读写文件的安全

   public class WRFile {
         //String str;
         boolean flag;
         public WRFile(){
             
         }
         
         public synchronized void read1(){
             if(this.flag){
                 try {
                     this.wait();
                 } catch (InterruptedException e) {
                     
                     e.printStackTrace();
                 }
             }
             RandomAccessFile ra = null;
             try {
                 ra = new RandomAccessFile("love.txt", "rw");
                 ra.seek(ra.length());
             
                 ra.writeBytes("I love you");
                 ra.writeBytes("\r\n");
             } catch (Exception e) {
                 e.printStackTrace();
             }
             finally {
                 try {
                     ra.close();
                 } catch (IOException e) {
                     
                     e.printStackTrace();
                 }
             }
             //修改标记 唤醒线程
             this.flag = true;
             this.notify();
         }
         
         public synchronized void read2(){
             if(!this.flag){
                 try {
                     this.wait();
                 } catch (InterruptedException e) {
                     
                     e.printStackTrace();
                 }
             }
             RandomAccessFile ra = null;
             try {
                 ra = new RandomAccessFile("love.txt", "rw");
                 ra.seek(ra.length());
             
                 ra.writeBytes("so do I");
                 ra.writeBytes("\r\n");
             } catch (Exception e) {
                 
                 e.printStackTrace();
             } finally {
                 try {
                     ra.close();
                 } catch (IOException e) {
                     
                     e.printStackTrace();
                 }
             }
             //修改标记 唤醒线程
             this.flag = false;
             this.notify();
         }

}
public class FirstThread implements Runnable {
      private WRFile wr = new WRFile();
      
         public FirstThread(WRFile wr) {
              this.wr = wr;
          } 
          
          @Override
          public void run() {
              
              while(true){
                  wr.read1();
              }
         }
  }
public class SecondThrad implements Runnable{
      private WRFile wr = new WRFile();
      public SecondThrad(WRFile wr) {
          this.wr = wr;
      }
      @Override
      public void run() {
          while(true)
          {
              wr.read2();
          }
      }
}
public static void main(String[] args) {
      //创建数据对象
      WRFile wr = new WRFile();
      //设置和获取类
      FirstThread ft = new FirstThread(wr);
      SecondThrad st = new SecondThrad(wr);
      //线程类
      Thread th1 = new Thread(ft);
      Thread th2 = new Thread(st);
      //启动线程
      th1.start();
      th2.start();
  }

145天以来,Java架构更新了 428个主题,已经有91位同学加入。微信扫码关注java架构,获取Java面试题和架构师相关题目和视频。上述相关面试题答案,尽在Java架构中。

上一篇 下一篇

猜你喜欢

热点阅读