Jstack日志分析定位死锁问题
前面我们讲解了Jstack查看线程状态,通过jstack日志,我们可以看到线程的启动状态,包括Runnable,Block及Waiting
那么从jstack 日志, 我们可以分析出来什么? 或者从日志里我们是否可以解决线程阻塞的问题?
1.Jstack日志信息
Jstack日志又叫 线程堆栈Dump 文件, 我们执行 jstack -pid 就是把现在系统运行的线程的堆栈打印出来, 下面我们讲解下 日志如何分析
- Runnable,线程处于执行中, 运行的线程 不需要关注
- Deadlock,
死锁(重点关注) - Blocked,
线程被阻塞(重点关注) - Parked,停止
- Locked,对象加锁
- Waiting,线程正在等待
- Waiting to lock 等待上锁
- Object.wait(),对象等待中
- Waiting for monitor entry
等待获取监视器(重点关注) - Waiting on condition,
等待资源(重点关注)
如果线程打印出来出来需要重点关注的状态, 这些线程就是有问题的,是需要我们排查的,否则就会导致系统长时间等待,无响应的场景,或者造成CPU过高的问题, 下面我们一一讲解下 这几种状态及问题定位和解决方案
2.DeadLock 死锁
什么是DeadLock死锁?
死锁就是 相互依赖的多方,因为竞争资源,都无法得到相应的资源, 而导致程序假死, 表现在项目中就是最终程序无响应
死锁的四个必要条件
- 互斥条件
- 资源互斥使用,资源的分配具有排他性,即当资源对象被一个线程使用(占有)时,别的线程都无法使用,只能够等待
- 该资源不可剥夺
- 资源不可剥夺,也就是说资源无法被资源请求者强制抢夺,所以占有者在使用完资源前,无法被其他进程强行夺走
- 必须等待资占有者主动释放,可剥夺资源的竞争是不会引起死锁的
- 请求和保持
- 进程中如果已经存在了一部分资源,当资源请求者在请求其他的资源的同时
- 请求者还保持对自己原有资源的占有,不释放持有的资源
- 循环等待
- 即存在一个等待队列,每一个进程所需要的资源,被下一个进程持有不释放,如T1等待占有T2的资源,T2等待占有T3的资源,T3等待占有T1的资源, 多方互相循环, 形成了一个等待环路。
3.如何避免死锁
上面我们了解了死锁的四个必要条件,那么在实际的项目中,或者开发过程中,我们如何避免死锁呢?
我们只需要打破其中任意一个条件即可避免死锁问题,下面我们讲解下如何打破这四个条件
- 破坏 互斥条件 无法成立
- 该条件是资源本身决定的,如果资源不互斥,那么就是每个进程都可以使用,那么也就不存在死锁了,因为每个进程都能拿到资源
- 破环 资源不可剥夺 条件
- 既然要破坏资源不可剥夺条件,那么我们让资源可以被剥夺,也就是允许其他进程在请求资源时候,对该资源进行抢夺,这就破坏了不可剥夺的条件
- 方法一 如果资源请求者,请求资源无法获取资源,那么它就要释放自身持有的资源,以便别人可以使用自己的资源
- 方法二 可以设置线程优先级,对资源来说,线程优先级高的进程,优先分配资源
- 方法三 加时请求,线程再请求资源的时候,要有时间限制,比如过了多久,还没有拿到该资源的时候,线程主动不再请求该资源,同时释放自己占有的锁。
- 破坏 请求和保持 条件
- 破坏请求和保持条件,就是不允许资源请求者在自己已经获取部分资源的情况下,申请其他资源,组织进程持有资源的同时,请求其他资源
- 方法一 一次性分配,就是进程既然无法在持有资源情况下再次申请其他资源,那么要么不分配,要么一次性分配,如果现在资源充足,那么就给你所有的资源,你也不需要再去请求其他资源,也就不会请求保持
- 方法二 先释放后申请,资源请求者再对新的资源提出申请前,必须释放自己占有的资源,这样也破坏了请求和保持条件
- 破坏 循环等待 条件
- 循环等待条件,是存在等待环路,我们如果让资源统一分配,把系统所有的资源全都编号,统一分配,所有的进程必须按照资源编号的顺序进行申请,这样就不会出现 等待环路,自然也不会出现死锁条件
本文 讲解我们分析 jstack 日志的时候,对于死锁问题的处理,分析了死锁产生的条件和常用的避免死锁的方法,破坏其四个必要条件避免死锁,下一篇,我们来分析下 死锁问题定位代码和jstack日志