面试七、多线程之synchronized和死锁
1、synchronized锁升级
偏向锁:适用于基本没有锁竞争的场景。大多数情况下都是不存在竞争的,都是同一个线程多次获取锁。
流程:
偏向锁对象会记录线程id,当每次有线程获取锁时会比较线程id如果一致则获得锁,
如不一致则查看原线程是否存活,如没有存活了则新线程获取到偏向锁=并记录新线程id
轻量级锁:适用于竞争锁的线程数量不多,锁的时间不长的场景。
竞争线程不会阻塞而是一直自旋知道获取到锁,频繁自旋会造成大量资源占用
获取锁流程:
线程1竞争锁,在线程1栈上会压入一个栈帧,栈帧里存放了lock Record(锁记录)
锁对象会复制一份markwork存入lock Record中,将lock Record中的owner指向锁对象,
将锁对象的指针cas修改为指向lock Record。
1、线程2如果是在线程1修改锁对象指针之后来竞争锁,发现锁指针已经有数据,
校验指针指的是否是自己本身线程,如果是则重入,否则自旋等待锁释放。
2、线程2如果是在线程1修改锁对象指针之前来竞争锁,发现锁指针没有数据,
在线程2栈上会压入一个栈帧,栈帧里存放了Lock Record,
锁对象复制一份markwork,存入lock Record中,将lock Record中的owner指向锁对象,
将锁对象的指针cas修改为指向lock Record。
这个时候线程2cas修改会失败,因为锁对象里的指针已经有值了,自旋等待锁释放
解锁流程:
cas将复制的markwork恢复到锁对象,成功则释放锁,失败说明锁已经膨胀为重量级锁
重量级锁:使用竞争锁的线程多。竞争线程会阻塞,等待锁释放
偏向锁->轻量级锁:
多个线程竞争一个锁
轻量级锁->重量级锁:
自旋多次失败
注意:锁只会升级,不会降级
2、死锁
线程1需要依次获取a、b两个锁,线程2需要依次获取b、a两个锁。
当线程1拿到a锁,线程2拿到b锁时,就出现死锁的情况
预防死锁:
2.1、以确定的顺序获得锁:获取锁的顺序永远是a、b
2.2、超时放弃:获取锁超时时间抛出异常
检查死锁:
jstack:
第一步:jps -l查看所有java进程,找到DeadLock的pid
第二步:jstack -l PID查看具体信息
jconsole:
有图形界面
posted on 2021-08-26 16:41 Iversonstear 阅读(268) 评论(0) 编辑 收藏 举报