锁的升级和比较
- 无状态锁(01)–>偏心锁(10)–>轻量级锁(00)(自旋锁)–>重量级锁
- 偏心锁:即偏向于第一个获得该对象锁的线程,如果在这个线程获得偏向锁后,之后只要该锁没有被别的线获取,则持有该偏向锁的线程将永远不需要被同步
- 偏向锁的MarkWord中包含:线程ID、epoch、对象分代年龄、是否偏心锁(1)、锁标志位(01)
- 偏向锁的获得:
- 当锁对象第一次被获取时,如线程1访问同步快,虚拟机会将对象头中Markword中的标志位设为01,并使用CAS操作将线程1的id保存在对象头的markword中
- 之后线程1每次访问该同步块时只需要检查markword中是否保存该线程id,有的话直接进入同步块执行.
- 如果锁对象的markword中没有保存线程1的id,则使用CAS进行替换,替换成功同样进入同步块执行.
- 偏向锁的升级
- 假如线程2尝试访问该同步块,也会查看对象头中是否保存线程2的id,失败也使用CAS尝试替换
- 失败则暂停拥有锁的线程(得到安全点才暂停),然后检查线程是否处于活动状态,如果线程死了,则将对象头设置为无锁状态,如果还活着,则将该同步块执行完,解锁,将对象头的线程ID置为空,然后恢复到无锁状态(01),或升级成轻量级锁(00).
- 轻量级锁
- 轻量级锁是用来在没有多线程竞争的条件下,减少传统的重量级锁使用操作系统的互斥量产生的性能消耗,对象在获得轻量级锁失败时会先自旋,自旋失败后才会升级为重量级锁
- 轻量级锁的markword中包含指向栈中锁记录的指针,和一个锁标志位(00)* 获得过程:
- 首先在当钱线程的栈帧中创建用于存储锁记录的空间displaced mark word,并将对象头中的markword中的信息复制到这个空间中.
- 然后尝试使用CAS修改markword,让其指向当前线程栈帧中创建的displaced markword.成功表示当前没有线程竞争,则获得该轻量级锁
- 失败则表示当前有线程在竞争该锁,则升级成重量级锁
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wd8Jx9il-1602603311502)(http://ifeve.com/wp-content/uploads/2012/10/%E8%BD%BB%E9%87%8F%E7%BA%A7%E9%94%81.png)]
- 首先在当钱线程的栈帧中创建用于存储锁记录的空间displaced mark word,并将对象头中的markword中的信息复制到这个空间中.
- 第二个线程在竞争时会先尝试自旋获取,如果失败才会升级成重量级锁.
- 解锁过程,使用CAS将markword和displaced markwordt替换回来,成功则同步完成,失败说明又线程在竞争,那就需要在释放锁的同时唤醒所有被挂起的线程竞争重量级锁.
- 自旋锁 在多线程竞争时,为了降低调用系统指令进行线程阻塞带来的性能消耗,让等待的线程尝试进行自旋,这个自旋次数根据历史上该线程是否成功获取过该锁而定.
- 重量级锁,主要使用互斥量进行同步,基本手段就是synchronized关键字,在编译后会在同步块前后分别形成monitorenter和monitorexit两个字节码指令.这连个参数都需要一个对象引用参数, 不指定就是当前对象实例或class对象,在执行monitorenter时,首先尝试获取对象的锁,如果对象没被锁,或者当前线程已拥有那个对象的锁,把锁的计数器加一,相应执行monitorexit则会减一.当计数器为0锁被释放.如果获取失败,线程就进入阻塞状态.
- 重量级锁不会自旋
- 重量级锁的markword:指向互斥量的指针,锁标记位10
- 三种锁的比较
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sQOCJfOr-1602603311504)(http://i.imgur.com/f0zrFOx.png)]