本文主要讲解“Java锁的升级策略是什么”。感兴趣的朋友不妨看看。本文介绍的方法简单、快速、实用。让边肖学习“Java锁的升级策略是什么”!
这三种锁指的是锁的状态,它们特定于Synchronized关键字。JDK 1.6为了降低“重量级锁”的性能消耗,引入了“偏向锁”和“轻量锁”。锁有四种状态:解锁状态、偏置锁、轻量锁和重量锁。锁定状态由对象标题中的标记字标记:
锁可以升级但不能降级,这意味着偏置锁升级为轻量锁后不能降级为偏置锁。这种升级锁但不能降级的策略是为了提高获取和释放锁的效率。
重量级锁:根据底层操作系统的互斥锁,线程会被阻塞。
缺点:锁定和解锁需要从用户模式切换到内核模式,消耗大量性能。
轻量级锁:基于重量级锁进行优化(避免上下文切换,提高性能)。它假设多线程竞争是交错的,不会发生线程阻塞,因此上下文切换是多余的。
第一个特点:采用CAS操作进行锁定和解锁,由于轻量级锁的Lock Record存储在对象头和线程空间,锁定和解锁不需要上下文切换,性能消耗小。
第二个特点:一旦发生多线程竞争,首先基于“自旋锁”的思想,自旋CPU会等待一段时间而不进行上下文切换,如果仍然无法获得锁,那么锁将升级为重量级锁。
偏锁:基于轻量锁进行优化(减少多次加锁和解锁,提升性能)。它假设在整个过程中只有一个线程获得锁,因此多次锁定和解锁是多余的。
特点:第一次获取锁后不会释放锁,会一直持有锁,以后进入锁时只需要检查锁状态和偏置的线程ID是否是自己的,省去了多次加锁和解锁。
1.偏向锁
获取锁定:
检查对象头的Mark Word是否可偏转(即是否偏向锁1,锁标志位是否为01),如果不是,尝试争锁:尝试CAS操作,将Mark Word的线程ID设置为当前线程ID,表示线程已经获得锁,如果失败,则说明锁已经被占用。
如果是可偏转的,检查线程标识是否是当前线程标识。如果是,说明当前线程已经持有锁(锁可以重新进入),否则说明锁已经被占用。
如果锁已被占用,您只能将偏置锁撤销为无锁或轻量锁。
释放锁:(bias锁采用等待竞争释放锁的机制,线程不会主动释放bias锁,只有当其他线程竞争bias锁时,持有Bias锁的线程才会释放锁)
有偏锁的撤销需要等待全局安全点(此时没有正在执行的字节码),停止持有有偏锁的线程,检查持有有偏锁的线程是否还活着。
如果线程挂起,将对象头设置为解锁状态;如果线程还活着,将对象头设置为轻量级锁(锁升级),轻量级锁最终会被释放。
2.轻量级锁
获取锁定:
检查对象头的Mark Word是否是轻量级锁(锁标志位为00),如果不是,尝试竞争锁:JVM首先在当前线程的栈帧中建立一个Lock Record,用于备份存储对象头的Mark Word(这个副本的官方前缀被一个Displaced mark word替换)。然后JVM尝试CAS操作,将标记字更新为指向锁记录的指针,以指示线程已经获得锁。如果失败,说明锁已经被占用了。
如果是轻量级锁,则判断对象头的Mark Word是否指向当前线程栈帧的Lock Record。如果是,则表示当前线程已经持有锁(锁可以重新进入),否则表示锁已经被占用。
如果锁已被占用,当前线程将尝试旋转CPU以获取锁。旋转一定次数后,轻量级锁将扩展为重量级锁(锁标志位变为10),线程将进入块。
释放锁定:
尝试CAS操作,将移位的标记字替换回目标
如果CAS操作失败,意味着有锁竞争,锁已经膨胀成了重量级的锁。有必要在释放锁的同时唤醒挂起的线程。
3.重量级锁
重量级锁依赖于底层操作系统的Mutex Lock,所有线程都会被阻塞,线程之间的切换需要从用户状态切换到内核状态,成本非常高。
总结:锁的优缺点比较
有偏锁锁定和解锁不需要额外的消耗,与异步方法相比只有纳秒的差距。如果线程之间存在锁竞争,就会带来额外的锁取消。适用于只有一个线程访问轻量级Lock竞争的线程,不会阻塞,提高了程序的响应速度。对于得不到锁的线程,spin会消耗CPU追求的响应时间,或者要求临界区短,Spin不会占用CPU太长时间。重量级Lock线程竞争不会使用spin,不会消耗CPU资源,线程阻塞,且响应时间慢会追求吞吐量。
至此,相信大家对“Java锁的升级策略是什么”有了更深的理解,让我们在实践中去做吧!这是网站。更多相关内容,可以去相关渠道查询,关注我们,继续学习!
内容来源网络,如有侵权,联系删除,本文地址:https://www.230890.com/zhan/105120.html