面试七、多线程之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编辑  收藏  举报

导航