内存飙升,罪魁祸首竟是死锁,这样检测和处理减少一半加班时间( 三 )


 
3、通过 jstack PID 查看进程信息
 

内存飙升,罪魁祸首竟是死锁,这样检测和处理减少一半加班时间

文章插图
 
接下来的信息:在jstack输出的信息中最后出现了死锁提示,提示显示在DeadThread.java文件的第39行和23行,那你去排查代码就可以啦
 
内存飙升,罪魁祸首竟是死锁,这样检测和处理减少一半加班时间

文章插图
 
方式2:通过jconsole工具这个工具在查看JVM内存时也是可以使用的,它是JDK中携带官方提供的工具,无需下载第三方插件即可使用
1、打开 jconsole 工具,在命令行输入jconsole即可开启,箭头右侧就是该工具启动页
 
内存飙升,罪魁祸首竟是死锁,这样检测和处理减少一半加班时间

文章插图
 
2、选择对应的Java进程查看信息,双击选中PID为128的Java进程
 
内存飙升,罪魁祸首竟是死锁,这样检测和处理减少一半加班时间

文章插图
 
3、选中线程,点击下方检查死锁按钮
 
内存飙升,罪魁祸首竟是死锁,这样检测和处理减少一半加班时间

文章插图
 
4、死锁检测结果,也会将死锁的信息展示出开【右侧信息需要双击左侧线程名才会展示出来】
 
内存飙升,罪魁祸首竟是死锁,这样检测和处理减少一半加班时间

文章插图
 
如何避免死锁这里说的避免死锁,其实是在生产环境中也就是项目上线运行不要出现死锁,不然又要被喊过去加班了,上边说了死锁产生的四个条件,只要我们将这四个条件中的任意一个破坏就不会产生死锁 。
  • 禁止一个线程同时持有多把锁
  • 具备相同的加锁顺序
  • 设置锁超时
  • 死锁检测
方案1:具备相同加锁顺序比如,企业和面试者的案例,调换两者加锁顺序一致即可解决死锁问题
package com.tianzhen.thread;public class DeadThread {// 工作锁private static Object work = new Object();// 工作经验锁private static Object workExperience = new Object();public static void main(String[] args) {// 企业线程new Thread(() -> {// 工作synchronized (work) {System.out.println(Thread.currentThread().getName() + "来吧!加入我们,有无经验都可");// 工作经验synchronized (workExperience) {System.out.println(Thread.currentThread().getName() + "感谢你的加入,为我们注入新鲜血液");}}},"企业线程:").start();// 员工线程new Thread(() -> {// 先获取工作机会synchronized (work) {System.out.println(Thread.currentThread().getName() + "我没有工作经验");// 有工作经验synchronized (workExperience) {System.out.println(Thread.currentThread().getName() + "通过工作获得了工作经验");}}},"面试者线程:").start();}}运行结果:
 
内存飙升,罪魁祸首竟是死锁,这样检测和处理减少一半加班时间

文章插图
 
此时就不会出现死锁,当企业线程运行占用work锁,这是如果发生线程切换,面试者也是要获取work锁,此时发现获取不到,就会进入阻塞,CPU放弃执行转而执行企业线程,此时企业线程获取workExperience锁,因为加锁顺序相同,此锁必然没有被比别的线程占用可以获得,继续执行,但是此时就无法实现交替执行,如果需要交替执行则需要使用线程通信实现,后边会安排此部分内容
方案2:设置超时因为 synchronized 不会自动释放,无法设置超时时间,此方案需要通过Lock接口实现,改接口在Java并发编程合集的《Java线程安全问题和解决方案》一文中有详细介绍
  • 通过tryLock尝试获取锁,如果获取不到就立即失败,不进入阻塞,你也可以调用tryLock(long time, TimeUnit unit)方法,设置获取所得超时时间,如果指定的时间没有获取到则继续运行
  • 在finally中记得通过unlock方法释放锁,如果不释放锁,就会一直持有,陷入死锁
package com.tianzhen.thread;import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReentrantLock;public class DeadThread {// 工作锁private static Lock work = new ReentrantLock();// 工作经验锁private static Lock workExperience = new ReentrantLock();public static void main(String[] args) {// 企业线程new Thread(() -> {// 工作if (workExperience.tryLock()) {try {System.out.println(Thread.currentThread().getName() + "没有工作经验,立即失败!");// 工作锁if (work.tryLock()) {try {System.out.println(Thread.currentThread().getName() + "技术不行,立即失败!");} finally {work.unlock();}}System.out.println(Thread.currentThread().getName() + "有工作经验,通过面试,欢迎加入我们!");} finally {workExperience.unlock();}}}, "企业线程:").start();// 员工线程new Thread(() -> {if (work.tryLock()) {try {System.out.println(Thread.currentThread().getName() + "我需要工作,才能有工作经验!");// 工作锁if (workExperience.tryLock()) {try {System.out.println(Thread.currentThread().getName() + "没有工作经验,告辞告辞!");} finally {workExperience.unlock();}}System.out.println(Thread.currentThread().getName() + "有工作经验,获取工作!");} finally {work.unlock();}}}, "面试者线程:").start();}}


推荐阅读