Java面试题:解释死锁的概念,给出避免死锁的常见策略。你能给我一个具体的例子吗?

简介: Java面试题:解释死锁的概念,给出避免死锁的常见策略。你能给我一个具体的例子吗?

死锁(Deadlock)是多线程编程中的一种现象,指的是两个或多个线程永久性地阻塞,每个线程等待其他线程释放锁,但是这些锁又被其他线程持有,导致没有任何线程能够继续执行,从而导致程序无法前进。

死锁通常发生在以下四个条件同时满足时:

  1. 互斥条件:资源不能被多个线程共同使用,只能由一个线程独占。
  2. 持有和等待条件:线程至少持有一个资源,并且正在等待获取额外的资源,而该资源又被其他线程持有。
  3. 非抢占条件:线程持有的资源在未使用完毕前不能被其他线程强行抢占。
  4. 循环等待条件:存在一个线程与资源之间的循环等待链,每个线程都在等待下一个线程所持有的资源。
    为了避免死锁,可以采取以下策略:
  5. 资源有序分配:规定所有线程按照相同的顺序请求资源,这样就可以避免循环等待链的形成。
  6. 一次性请求所有资源:线程在开始执行前一次性请求所有需要的资源,这样就可以避免持有部分资源而等待其他资源的情况。
  7. 超时等待:线程在请求资源时设置超时时间,如果在超时时间内没有获得资源,则放弃等待,回退并重新尝试。
  8. 资源预分配:预先分配一定量的资源给线程,减少线程在运行时对资源的动态请求。
  9. 锁排序:线程在请求锁时,按照一定的规则进行排序,确保锁的请求和释放顺序一致。
  10. 死锁检测和恢复:运行时检测死锁的发生,并采取措施解除死锁,例如撤销某个线程或回滚操作。

使用并发工具:使用Java并发API中的工具类,如ReentrantLock,它提供了尝试锁定和定时锁定,以及锁的可中断特性,这些都有助于避免死锁。


  1. 通过合理的设计和编码实践,可以有效地避免死锁的发生,确保多线程程序的稳定性和可靠性。

当然可以。以下是一个简单的Java示例,它展示了死锁是如何发生的:

public class DeadlockDemo {
    private final Object lock1 = new Object();
    private final Object lock2 = new Object();
    public void method1() {
        synchronized (lock1) {
            System.out.println("Thread " + Thread.currentThread().getId() + " holds lock1 and waiting for lock2");
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (lock2) {
                System.out.println("Thread " + Thread.currentThread().getId() + " holds lock1 and lock2");
            }
        }
    }
    public void method2() {
        synchronized (lock2) {
            System.out.println("Thread " + Thread.currentThread().getId() + " holds lock2 and waiting for lock1");
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (lock1) {
                System.out.println("Thread " + Thread.currentThread().getId() + " holds lock1 and lock2");
            }
        }
    }
    public static void main(String[] args) {
        DeadlockDemo demo = new DeadlockDemo();
        Thread t1 = new Thread(demo::method1);
        Thread t2 = new Thread(demo::method2);
        t1.start();
        t2.start();
    }
}

在这个例子中,我们有两个方法method1method2,它们各自尝试获取两个不同的锁。线程t1开始执行method1,它获得了lock1,然后尝试获取lock2。同时,线程t2开始执行method2,它获得了lock2,然后尝试获取lock1

由于这两个方法中的锁请求都是先获得一个锁,然后尝试获取另一个锁,而且它们都没有设置超时,所以它们都会无限期地等待对方释放锁。这样,两个线程都持有所获得的锁,并且都在等待对方释放锁,导致了死锁的发生。

运行这个程序,你可能会看到输出类似于以下内容:

Thread 1 holds lock1 and waiting for lock2
Thread 2 holds lock2 and waiting for lock1

在这种情况下,程序将无法继续执行,因为两个线程都无法继续执行它们的操作,因为他们互相等待对方释放锁。

要避免这种死锁,我们可以采取一些策略,比如设置锁请求的超时时间,或者确保线程在获取锁时的顺序一致。

相关文章
|
3月前
|
算法 Java
50道java集合面试题
50道 java 集合面试题
|
3月前
|
算法 Java
50道java基础面试题
50道java基础面试题
|
5月前
|
Java 数据库连接 数据库
Java 相关知识点总结含基础语法进阶技巧及面试重点知识
本文全面总结了Java核心知识点,涵盖基础语法、面向对象、集合框架、并发编程、网络编程及主流框架如Spring生态、MyBatis等,结合JVM原理与性能优化技巧,并通过一个学生信息管理系统的实战案例,帮助你快速掌握Java开发技能,适合Java学习与面试准备。
251 2
Java 相关知识点总结含基础语法进阶技巧及面试重点知识
|
5月前
|
缓存 Java 关系型数据库
Java 面试经验总结与最新 BAT 面试资料整理含核心考点的 Java 面试经验及最新 BAT 面试资料
本文汇总了Java面试经验与BAT等大厂常见面试考点,涵盖心态准备、简历优化、面试技巧及Java基础、多线程、JVM、数据库、框架等核心技术点,并附实际代码示例,助力高效备战Java面试。
195 0
|
5月前
|
缓存 Cloud Native Java
Java 面试微服务架构与云原生技术实操内容及核心考点梳理 Java 面试
本内容涵盖Java面试核心技术实操,包括微服务架构(Spring Cloud Alibaba)、响应式编程(WebFlux)、容器化(Docker+K8s)、函数式编程、多级缓存、分库分表、链路追踪(Skywalking)等大厂高频考点,助你系统提升面试能力。
267 0
|
5月前
|
关系型数据库 MySQL Java
字节面试: MySQL 百万级 导入发生的 “死锁” 难题如何解决?“2序4拆”,彻底攻克
字节面试: MySQL 百万级 导入发生的 “死锁” 难题如何解决?“2序4拆”,彻底攻克
字节面试: MySQL 百万级 导入发生的 “死锁” 难题如何解决?“2序4拆”,彻底攻克
|
5月前
|
缓存 Java API
Java 面试实操指南与最新技术结合的实战攻略
本指南涵盖Java 17+新特性、Spring Boot 3微服务、响应式编程、容器化部署与数据缓存实操,结合代码案例解析高频面试技术点,助你掌握最新Java技术栈,提升实战能力,轻松应对Java中高级岗位面试。
475 0
|
2月前
|
JSON 网络协议 安全
【Java】(10)进程与线程的关系、Tread类;讲解基本线程安全、网络编程内容;JSON序列化与反序列化
几乎所有的操作系统都支持进程的概念,进程是处于运行过程中的程序,并且具有一定的独立功能,进程是系统进行资源分配和调度的一个独立单位一般而言,进程包含如下三个特征。独立性动态性并发性。
177 1
|
2月前
|
JSON 网络协议 安全
【Java基础】(1)进程与线程的关系、Tread类;讲解基本线程安全、网络编程内容;JSON序列化与反序列化
几乎所有的操作系统都支持进程的概念,进程是处于运行过程中的程序,并且具有一定的独立功能,进程是系统进行资源分配和调度的一个独立单位一般而言,进程包含如下三个特征。独立性动态性并发性。
203 1