流程:
1. 判断是否为可偏向状态–MarkWord中锁标志是否为‘01’,是否偏向锁是否为‘1’
2. 如果是可偏向状态,则查看线程ID是否为当前线程,如果是,则进入步骤 ‘V’,否则进入步骤‘III’
3. 通过CAS操作竞争锁,如果竞争成功,则将MarkWord中线程ID设置为当前线程ID,然后执行‘V’;竞争失败,则执行‘IV’
4. CAS获取偏向锁失败表示有竞争。当达到safepoint时获得偏向锁的线程被挂起,偏向锁升级为轻量级锁,然后被阻塞在安全点的线程继续往下执行同步代码块
5. 执行同步代码
轻量级锁是相对于重量级锁需要阻塞/唤醒涉及上下文切换而言,主要针对多个线程在不同时间请求同一把锁的场景。轻量级锁获取过程:
-
进行加锁操作时,jvm会判断是否已经是重量级锁,如果不是,则会在当前线程栈帧中划出一块空间,作为该锁的锁记录,并且将锁对象MarkWord复制到该锁记录中
-
复制成功之后,jvm使用CAS操作将对象头MarkWord更新为指向锁记录的指针,并将锁记录里的owner指针指向对象头的MarkWord。如果成功,则执行‘III’,否则执行‘IV’
-
更新成功,则当前线程持有该对象锁,并且对象MarkWord锁标志设置为‘00’,即表示此对象处于轻量级锁状态
-
更新失败,jvm先检查对象MarkWord是否指向当前线程栈帧中的锁记录,如果是则执行‘V’,否则执行‘VI’
-
表示锁重入;然后当前线程栈帧中增加一个锁记录第一部分(Displaced Mark Word) 为null,并指向Mark Word的锁对象,起到一个重入计数器的作用
-
表示该锁对象已经被其他线程抢占,则进行自旋等待(默认10次),等待次数达到阈值仍未获取到锁,则升级为重量级锁
当有多个锁竞争轻量级锁则会升级为重量级锁,重量级锁正常会进入一个cxq的队列,在调用wait方法之后,则会进入一个waitSet的队列park等待,而当调用notify方法唤醒之后,则有可能进入EntryList。重量级锁加锁过程:
-
分配一个ObjectMonitor对象,把Mark Word锁标志置为‘10’,然后Mark Word存储指向ObjectMonitor对象的指针。ObjectMonitor对象有两个队列和一个指针,每个需要获取锁的线程都包装成ObjectWaiter对象
-
多个线程同时执行同一段同步代码时,ObjectWaiter先进入EntryList队列,当某个线程获取到对象的monitor以后进入Owner区域,并把monitor中的owner变量设置为当前线程同时monitor中的计数器count+1;
10.简单描述一下ABA问题?
===============
1.有两个线程同时去修改一个变量的值,比如线程1、线程2,都更新变量值,将变量值从A更新成B。
2.首先线程1、获取到CPU的时间片,线程2由于某些原因发生阻塞进行等待,此时线程1进行比较更新(CompareAndSwap),成功将变量的值从A更新成B。
3.更新完毕之后,恰好又有线程3进来想要把变量的值从B更新成A,线程3进行比较更新,成功将变量的值从B更新成A。 4. 线程2获取到CPU的时间片,然后进行比较更新,发现值是预期的A,然后又更新成了B。但是线程1并不知道,该值已经有了A->B->A这个过程,这也就是我们常说的ABA问题。
4.可以通过加版本号或者加时间戳解决,或者保证单向递增或者递减就不会存在此类问题。
11.实现一下DCL?
===========
12.实现一个阻塞队列(用Condition写生产者与消费者就)?
=================================
13.实现多个线程顺序打印abc?
=================
14.服