Java核心技术面试精讲
杨晓峰
前Oracle首席工程师
立即订阅
43250 人已学习
课程目录
已完结 43 讲
0/4登录后,你可以任选4讲全文学习。
开篇词 (1讲)
开篇词 | 以面试题为切入点,有效提升你的Java内功
免费
模块一 Java基础 (14讲)
第1讲 | 谈谈你对Java平台的理解?
第2讲 | Exception和Error有什么区别?
第3讲 | 谈谈final、finally、 finalize有什么不同?
第4讲 | 强引用、软引用、弱引用、幻象引用有什么区别?
第5讲 | String、StringBuffer、StringBuilder有什么区别?
第6讲 | 动态代理是基于什么原理?
第7讲 | int和Integer有什么区别?
第8讲 | 对比Vector、ArrayList、LinkedList有何区别?
第9讲 | 对比Hashtable、HashMap、TreeMap有什么不同?
第10讲 | 如何保证集合是线程安全的? ConcurrentHashMap如何实现高效地线程安全?
第11讲 | Java提供了哪些IO方式? NIO如何实现多路复用?
第12讲 | Java有几种文件拷贝方式?哪一种最高效?
第13讲 | 谈谈接口和抽象类有什么区别?
第14讲 | 谈谈你知道的设计模式?
模块二 Java进阶 (16讲)
第15讲 | synchronized和ReentrantLock有什么区别呢?
第16讲 | synchronized底层如何实现?什么是锁的升级、降级?
第17讲 | 一个线程两次调用start()方法会出现什么情况?
第18讲 | 什么情况下Java程序会产生死锁?如何定位、修复?
第19讲 | Java并发包提供了哪些并发工具类?
第20讲 | 并发包中的ConcurrentLinkedQueue和LinkedBlockingQueue有什么区别?
第21讲 | Java并发类库提供的线程池有哪几种? 分别有什么特点?
第22讲 | AtomicInteger底层实现原理是什么?如何在自己的产品代码中应用CAS操作?
第23讲 | 请介绍类加载过程,什么是双亲委派模型?
第24讲 | 有哪些方法可以在运行时动态生成一个Java类?
第25讲 | 谈谈JVM内存区域的划分,哪些区域可能发生OutOfMemoryError?
第26讲 | 如何监控和诊断JVM堆内和堆外内存使用?
第27讲 | Java常见的垃圾收集器有哪些?
第28讲 | 谈谈你的GC调优思路?
第29讲 | Java内存模型中的happen-before是什么?
第30讲 | Java程序运行在Docker等容器环境有哪些新问题?
模块三 Java安全基础 (2讲)
第31讲 | 你了解Java应用开发中的注入攻击吗?
第32讲 | 如何写出安全的Java代码?
模块四 Java性能基础 (3讲)
第33讲 | 后台服务出现明显“变慢”,谈谈你的诊断思路?
第34讲 | 有人说“Lambda能让Java程序慢30倍”,你怎么看?
第35讲 | JVM优化Java代码时都做了什么?
模块5 Java应用开发扩展 (4讲)
第36讲 | 谈谈MySQL支持的事务隔离级别,以及悲观锁和乐观锁的原理和应用场景?
第37讲 | 谈谈Spring Bean的生命周期和作用域?
第38讲 | 对比Java标准NIO类库,你知道Netty是如何实现更高性能的吗?
第39讲 | 谈谈常用的分布式ID的设计方案?Snowflake是否受冬令时切换影响?
周末福利 (2讲)
周末福利 | 谈谈我对Java学习和面试的看法
周末福利 | 一份Java工程师必读书单
结束语 (1讲)
结束语 | 技术没有终点
Java核心技术面试精讲
登录|注册

第18讲 | 什么情况下Java程序会产生死锁?如何定位、修复?

杨晓峰 2018-06-16
今天,我会介绍一些日常开发中类似线程死锁等问题的排查经验,并选择一两个我自己修复过或者诊断过的核心类库死锁问题作为例子,希望不仅能在面试时,包括在日常工作中也能对你有所帮助。
今天我要问你的问题是,什么情况下 Java 程序会产生死锁?如何定位、修复?

典型回答

死锁是一种特定的程序状态,在实体之间,由于循环依赖导致彼此一直处于等待之中,没有任何个体可以继续前进。死锁不仅仅是在线程之间会发生,存在资源独占的进程之间同样也可能出现死锁。通常来说,我们大多是聚焦在多线程场景中的死锁,指两个或多个线程之间,由于互相持有对方需要的锁,而永久处于阻塞的状态。
你可以利用下面的示例图理解基本的死锁问题:
定位死锁最常见的方式就是利用 jstack 等工具获取线程栈,然后定位互相之间的依赖关系,进而找到死锁。如果是比较明显的死锁,往往 jstack 等就能直接定位,类似 JConsole 甚至可以在图形界面进行有限的死锁检测。
如果程序运行时发生了死锁,绝大多数情况下都是无法在线解决的,只能重启、修正程序本身问题。所以,代码开发阶段互相审查,或者利用工具进行预防性排查,往往也是很重要的。

考点分析

今天的问题偏向于实用场景,大部分死锁本身并不难定位,掌握基本思路和工具使用,理解线程相关的基本概念,比如各种线程状态和同步、锁、Latch 等并发工具,就已经足够解决大多数问题了。
取消
完成
0/1000字
划线
笔记
复制
© 版权归极客邦科技所有,未经许可不得传播售卖。 页面已增加防盗追踪,如有侵权极客邦将依法追究其法律责任。
该试读文章来自付费专栏《Java核心技术面试精讲》,如需阅读全部文章,
请订阅文章所属专栏。
立即订阅
登录 后留言

精选留言(26)

  • 石头狮子
    1. 死锁的另一个好朋友就是饥饿。死锁和饥饿都是线程活跃性问题。
    实践中死锁可以使用 jvm 自带的工具进行排查。
    2. 课后题提出的死循环死锁可以认为是自旋锁死锁的一种,其他线程因为等待不到具体的信号提示。导致线程一直饥饿。
    这种情况下可以查看线程 cpu 使用情况,排查出使用 cpu 时间片最高的线程,再打出该线程的堆栈信息,排查代码。
    3. 基于互斥量的锁如果发生死锁往往 cpu 使用率较低,实践中也可以从这一方面进行排查。

    作者回复: 很好的总结

    2018-06-16
    80
  • I am a psycho
    当是死循环引起的其他线程阻塞,会导致cpu飙升,可以先看下cpu的使用率。

    作者回复: 对,比如Linux上,可以使用top命令配合grep Java之类,找到忙的pid;然后,转换成16进制,就是jstack输出中的格式;再定位代码

    2018-06-16
    29
  • curlev3
    回答老师的问题
    可以通过linux下top命令查看cpu使用率较高的java进程,进而用top -Hp ➕pid查看该java进程下cpu使用率较高的线程。再用jstack命令查看线程具体调用情况,排查问题。

    作者回复: 非常不错

    2018-06-26
    13
  • 黑子
    任务线程规范命名,详细记录逻辑运行日志。jstack查看线程状态。

    作者回复: 不错

    2018-06-16
    6
  • tracer
    看了下jconsole检测死锁功能的源码,果然也是用ThreadMXBean获取死锁线程并分组,然后打印相关线程信息的。
    2018-06-27
    5
  • 西鄉十六夜
    老师,面试遇到过一个很刁钻的问题。如何在jvm不重启的情况下杀死一个线程,在stop被移除后,如果线程存在死锁那是否意味着必须要修复代码再重启虚拟机呢?

    作者回复: 不知道有什么好办法,也许用我例子哪个API去找到死锁线程,想办法把死锁条件打开;但我觉得这东西不靠谱,假设真的解除死锁,你还能保证程序正确性吗,这不会是个通用解决方案

    另外,即使以前有stop方法,blocked状态的线程也是关不了的吧,它不响应你的请求的

    2018-07-10
    3
  • 肖一林
    初学nio的时候确实动不动就发生死锁。现在好像也没有特别好的教程,都是一些java.io的教程。很多教程跟不上技术的迭代。也可能是因为直接io编程在项目实践中偏少。

    另外,这个小程序的图片不能放大看,不知道是微信的原因还是小程序的原因。老师看到了帮忙反馈一下。

    作者回复: nio确实教程少,书籍也不好找 Java IO,NIO,NIO2好像也没引进;如果想系统学习,我建议买本 《netty实战》,Java自己的nio定位偏重于基础性API,与终端应用需求有点鸿沟

    2018-06-16
    3
  • jacy
    尽然可以用ThreadMXBean来抓线程死锁信息,受教了。
    循环死锁,会导致cpu某线程的cpu时间片占用率相当高,可以结合操作系统工具分析出线程号,然后用jstack分析线程

    作者回复: 不错

    2018-06-22
    2
  • 残阳
    以前做排查的时候看thread dump, 一般都会直接按一些关键字搜索。比如wait,lock之类,然后再找重复的内存地址。看完这遍文章之后感觉对死锁的理解更深刻。

    作者回复: 谢谢,地址也很重要

    2018-06-17
    2
  • vaccywen
    多执行几次jstack,如果同一个线程id多次结果都是running,而其他线程一直都是BLOCKED状态,是不是就可以判断某个持锁线程进入死循环?
    2019-11-04
    1
  • 豌豆逸之
    请教老师,线程饥饿的情况,有没有什么好的诊断办法?
    2019-08-09
    1
  • 肖一林
    一课一练:
    最典型的场景是nio的Selector类,这个类内部有三个集合,并且对这些集合做了同步。如果多个线程同时操作一个Selector,就很容易发生死锁。它的select方法会一直拿着锁,并且循环等待事件发生。如果有其他线程在修改它内部的集合数据,就死锁了。

    同样用jstack可以发现问题,找出被阻塞的线程,看它等待哪个锁,再找到持有这把锁的线程,这个线程一搬处于运行状态

    作者回复: 不错,selected key 和 cancelled key的集合不是线程安全的,我记得标准文档就建议

    2018-06-16
    1
  • Paul Shan
    死锁是两个线程相互试图获取对方独占的资源的情况。如果随机使用锁,这种情况不可避免。
    2019-11-12
  • 格非
    操作系统中学过进程死锁发成的四个必要条件:1、互斥条件,2、占有和等待条件,3、不可抢占条件,4、循环等待条件;破环这四个必要条件中的一个就可以避免发生死锁
    2019-10-12
  • 随心而至
    一:死锁的主要原因:
    不同线程获取锁的顺序不一致导致。
    二:解决方法:
    1.确保按照一定顺序获取锁,比如两个类似lockA, lockB的hash值比较,如果相等(概率很低),再添加一把另外的锁tieLock ,具体示例 参加 JCIP 10-3
    2.开放调用 不要同时获取多把锁 具体示例 参见JCIP 10-6
    3.使用定时的锁 ,tryLock() 或者tryLock(timeout)

    三:死锁检测:
    3.1 jstack pid > app.dump 然后在文件中查找线程状态(比如搜索Blocking) -> 查看等待目标 -> 对比 Monitor...
    3.2 ThreadMXBean



    2019-09-18
  • JSON
    如果是生产环境出现了死锁状态,该怎么排查问题
    2019-07-17
  • Geek_7eb30c
    老师有个问题咨询下,我在跑一个大批量数据导入数据库的程序,用线程池一批1000导入数据库,头尾用了分布式redis锁防止并发,但在倒入中还是出现锁表的情况,不知道是不是我的分布式锁放错位置,是否应该放在异步线程开启前加锁
    2019-06-10
  • QQ怪
    还有一种锁叫做活锁,可能两个线程一直在释放锁,抢占锁,互不相让,这种也是一种并发问题
    2019-04-01
  • 天使梦泪
    java8环境,连着多运行几次那个死锁实例代码才会出现blocked状态,前几次都是RUNNABLE状态,而且用线程组打印的才是blocked,直接用线程的getState方法打印的都是RUNNABLE。
    2019-02-28
  • 苦行僧
    平常工作发生的死锁都发生在数据库层面,多线程并发修改同一条记录
    2019-02-18
收起评论
26
返回
顶部