Java偏向锁实现原理(Biased Locking)

阅读本文的读者,需要对Java轻量级锁有一定的了解,知道lock record, mark word之类的名词。可以参考我的一篇博文:Java轻量级锁原理详解(Lightweight Locking)

Java偏向锁(Biased Locking)是Java6引入的一项多线程优化。它通过消除资源无竞争情况下的同步原语,进一步提高了程序的运行性能。

轻量级锁也是一种多线程优化,它与偏向锁的区别在于,轻量级锁是通过CAS来避免进入开销较大的互斥操作,而偏向锁是在无竞争场景下完全消除同步,连CAS也不执行(CAS本身仍旧是一种操作系统同步原语,始终要在JVM与OS之间来回,有一定的开销)。

所谓的无竞争场景,举个例子,就是单线程访问带同步的资源或方法。

偏向锁实现原理

偏向锁,顾名思义,它会偏向于第一个访问锁的线程,如果在接下来的运行过程中,该锁没有被其他的线程访问,则持有偏向锁的线程将永远不需要触发同步。
如果在运行过程中,遇到了其他线程抢占锁,则持有偏向锁的线程会被挂起,JVM会尝试消除它身上的偏向锁,将锁恢复到标准的轻量级锁。(偏向锁只能在单线程下起作用)

通过下图可以更直观的理解偏向锁:

这张图,省略了轻量级锁相关的几处步骤,将关注点更多地聚焦在偏向锁的状态变化上。
偏向模式和非偏向模式,在下面的mark word表中,主要体现在thread ID字段是否为空。

挂起持有偏向锁的线程,这步操作类似GC的pause,但不同之处是,它只挂起持有偏向锁的线程(非当前线程)。

在抢占模式的橙色区域说明中有提到,指向当前堆栈中最近的一个lock record(在轻量级锁原理一文有讲到,lock record是进入锁前会在stack上创建的一份内存空间)。
这里提到的最近的一个lock record,其实就是当前锁所在的stack frame上分配的lock record。
整个步骤是从偏向锁恢复到轻量级锁的过程。

偏向锁也会带来额外开销

在JDK6中,偏向锁是默认启用的。它提高了单线程访问同步资源的性能。
但试想一下,如果你的同步资源或代码一直都是多线程访问的,那么消除偏向锁这一步骤对你来说就是多余的。事实上,消除偏向锁的开销还是蛮大的。

所以在你非常熟悉自己的代码前提下,大可禁用偏向锁 -XX:-UseBiasedLocking 。

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 偏向锁、轻量级锁和重量级锁都是Java中的线程同步技术,它们主要的区别在于使用的场景和性能表现。 1. 偏向锁Biased Locking偏向锁是一种针对单线程访问的优化手段。当一个线程获取偏向锁时,JVM会将对象头的Mark Word信息修改为指向该线程,以后该线程再次获取该锁时,无需进行任何同步操作,直接进入临界区即可。只有当其他线程尝试获取锁时,才会将偏向锁升级为轻量级锁或重量级锁。 2. 轻量级锁(Lightweight Locking) 当多个线程竞争同一个锁时,JVM会尝试使用轻量级锁进行同步,它是一种乐观的锁策略。当线程获取锁失败时,JVM会使用CAS操作来尝试获取锁,如果CAS操作成功,则线程进入临界区。如果CAS操作失败,则表示有多个线程竞争同一个锁,此时JVM会将轻量级锁升级为重量级锁。 3. 重量级锁(Heavyweight Locking) 当多个线程竞争同一个锁时,JVM会使用重量级锁进行同步,它是一种悲观的锁策略。重量级锁采用互斥量(Mutex)来实现,当一个线程获取锁时,其他线程必须阻塞等待。重量级锁的性能表现最差,但是它可以保证线程安全性。 总的来说,偏向锁适用于单线程访问的情况,轻量级锁适用于短时间内多个线程的竞争情况,而重量级锁适用于长时间内多个线程的竞争情况。同时,偏向锁和轻量级锁的性能都比重量级锁要好,因为它们避免了线程的阻塞和唤醒操作。 ### 回答2: 偏向锁、轻量锁和重量锁都是Java中多线程同步机制中的一种实现方式。 偏向锁是针对无竞争情况下对同步代码块进行优化的机制。当一个线程访问同步代码块时,会尝试获取锁。如果此时锁对象没有被其他线程锁持有,并且没有发生竞争,即所谓的无竞争情况,那么该线程会将锁对象的对象头中的标记设置为偏向锁,并将线程ID记录在对象头中。此后,该线程再次进入同步代码块时就无需重新获取锁,可以直接进入。这样可以节省锁的竞争时间,提高执行效率。 轻量锁是针对短时间内有竞争但竞争情况不是很激烈的情况下对同步代码块进行优化的机制。当一个线程访问同步代码块时,如果发现锁对象的对象头中的标记是偏向锁,并且记录的线程ID与当前线程ID相同,那么表示该线程持有偏向锁,可以直接进入临界区。而如果发现锁对象的对象头中的标记是偏向锁,但记录的线程ID与当前线程ID不同,那么说明该对象有竞争,此时会通过CAS操作将锁对象的对象头替换为一个称为“轻量锁”的数据结构。轻量锁的获取和释放使用CAS操作,避免了使用互斥量的开销,从而提高了执行效率。 重量锁是当锁的竞争激烈,或者同步代码块长度较长的情况下,对同步代码块进行优化的机制。重量锁会将锁对象的对象头替换为一个互斥量,线程在进入临界区前需要先获取互斥量,离开临界区后需要释放互斥量,这样保证了临界区的互斥性。在竞争激烈的情况下,重量锁能够更好地确保同步代码的正确性,但相对于偏向锁和轻量锁,它的执行效率更低。 总结来说,偏向锁适用于无竞争环境,轻量锁适用于短时间内有竞争但竞争不激烈的环境,重量锁适用于竞争激烈或同步代码块较长的环境。 ### 回答3: 偏向锁,轻量锁和重量锁是Java虚拟机在并发编程中为了提高效率而设计的三种锁机制。 首先,偏向锁是为了解决线程同步过程中高并发情况下的性能问题而引入的。它的特点是,当只有一个线程访问同步块时,该线程会取得锁并在对象头部的标记字段中将对象的锁标记置为偏向锁,以后访问同步块时,无需再进行加锁解锁操作,从而提高了程序的运行效率。但如果多个线程争夺同一把偏向锁时,偏向锁会被升级为轻量锁。 轻量锁适用于多个线程交替执行同步块的场景。它的特点是,在开始访问同步块时,锁对象的对象头被复制到线程的线程状态字中,然后线程对对象进行操作,完成之后再将对象头复制回锁对象中。这个过程是一次乐观的自旋操作,如果所得到的锁对象没有被其他线程锁定,那么获取锁的线程可以直接进行操作,避免了线程阻塞和操作系统调度的开销。 重量锁是Java虚拟机默认的锁机制,适用于多个线程并发访问的场景。它的特点是,当多个线程争夺同一个锁时,锁对象的对象头中的锁标记字段被置为重量锁,线程会进入阻塞状态,将其他线程加入到一个同步队列中。在同步队列中唤醒的线程会检查锁是否被其他线程占用,如果锁空闲,则会尝试重新获取锁,否则继续进入阻塞状态。 总的来说,偏向锁适用于以大部分线程串行访问同步块为主的场景,轻量锁适用于多个线程交替执行同步块的场景,重量锁适用于多个线程并发访问的场景。这三种锁机制分别针对不同的场景选择了适合的优化策略,以提高程序的性能和效率。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值