精华内容
下载资源
问答
  • 偏向锁原理

    2020-09-24 22:30:59
    偏向锁原理 persisting_关注 0.0932019.01.26 18:58:40字数 2,554阅读 3,238 1 概述 本文介绍偏向锁相关原理,并不限定于Java中的偏向锁,但是Java中偏向锁的实现也是相同的原理,本文主要是对参考文献(Quickly ...

    偏向锁原理

    persisting_关注

    0.0932019.01.26 18:58:40字数 2,554阅读 3,238

    1 概述

    本文介绍偏向锁相关原理,并不限定于Java中的偏向锁,但是Java中偏向锁的实现也是相同的原理,本文主要是对参考文献(Quickly Reacquirable Locks)中偏向锁实现重点部分的翻译,加入了自己的理解,参考文献称偏向锁为可快速获取的锁(QRL,Quickly Reacquirable Locks)。如何快速获取将会在第2节介绍过相关数据结构之后介绍。偏向锁主要是为了提高绝大部分情况下不存在竞争、只有一个线程在尝试获取锁的场景,通过相关数据结构可以减少CAS操作的数量,提高应用性能。偏向锁在有多个线程竞争获取时,会变成(论文中称为revoke,撤销)普通的锁(或默认锁),在Java中则会变先变为轻量级锁。

    2 相关数据结构

    相对于一般的锁来说,偏向锁使用了两个额外的域(这里不能等同于Java类成员域,在Java中其实是使用对象头中的Mark Word保存的)。第一个域是用来记录锁当前状态的域,可能的状态包括NEURALBIASEDREVOKINGDEFAULT

    • NEURAL:锁的初始状态,刚建立时的状态。
    • BIASED:该锁被第一次获取且没有竞争时,锁状态会被持有的线程置为此状态。
    • REVOKING:如果有另一个线程尝试获取当前被其他线程持有的BIASED状态的锁,则该锁会经过撤销过程变为普通的锁,即DEFAULT,撤销过程中该锁会处于中间状态REVOKING
    • DEFAULT:表示因为多个线程竞争获取偏向锁,则该锁会退化为普通的锁。

    注意:撤销和释放(release)锁不同,这里的撤销(revoke)指的是从偏向锁退化为普通锁的过程。

    初始状态下锁处于NEURAL状态,当该锁被第一次获取时,获取锁的线程会将锁状态变为BIASED。此时如果有另一个线程尝试获取同一个锁,那么其最终会将锁状态变为DEFAULT,在变为DEFAULT状态之前该锁会根据撤销协议先处于一个暂时的中间状态REVOKING。当锁状态为BIASED时,状态中会有一个域用来记录当前持有锁的线程标识符(后文使用线程ID代替)。对于一个不可再偏向(non-rebiasable,表示一旦撤销之后不可再次回到偏向状态)的锁,则锁状态只能按照NEURAL->BIASED->REVOKING->DEFAULT的状态进行切换,不可回退。但是偏向锁也可实现为可重偏向的锁(rebiasable)。

    实现偏向锁需要额外增加的第二个域是一个可以指示布尔类型的位(bit)即可,用来表示偏向锁当前持有者对锁是获取状态还是释放状态。有了这个标识,当偏向锁被获取并且没有撤销时,当前持有者线程的获取和释放操作只要简单的改变该域的状态即可。

    注意,一个线程获取偏向锁首先需要成为偏向锁的持有者(holder),即将自己的线程ID通过CAS写入锁状态域中,然后再获取锁(acquire),持有并不一定获取了锁,持有之后必须改变上面布尔标识位才获取了锁,但是获取锁之前必须要先成为锁的持有者;释放(release)锁之后也需要有其他线程显示竞争之后当前持有者才会失去持有者身份,锁的持有者可以通过改变上面介绍的布尔位进行锁的获取和释放动作。

    将锁从初始的NEUTRAL状态转变为BIASED状态可以仅通过一次CAS操作完成。这里使用CAS操作是必须的,因为需要避免多个线程同时尝试获取NEUTRAL状态锁并置其状态为BIASED

    现在我们介绍第1节概述中快速获取的含义,根据上面的数据结构介绍,当一个线程获取了偏向锁之后,会在锁中记录线程ID,锁中也会有一个标识位用来表示该锁目前是释放的还是被获取的。因此以后线程在获取和释放锁时,只要检测锁是否记录自己的线程ID即可,如果检测成功,表示线程已经是偏向锁的持有者,直接通过改变上述布尔类型标识位域进行获取和释放锁即可。如果测试失败(表示该锁目前持有者不是自己),则再测试一下锁当前状态,如果还是BIASED状态,则使用CAS竞争锁,如果CAS成功,则尝试使用CAS将锁状态中线程ID置为自己的线程ID,这样后续在获取和释放锁时就不用使用CAS操作了。上面测试过程没有使用CAS操作,因此提高了性能,实现了快速获取锁。

    实现上面atomic-free(表示尽可能减少CAS这样的原子操作)偏向锁的难点就在于如何协调获取偏向锁和撤销偏向锁的过程。必须处理偏向锁获取和释放同时发生偏向锁撤销时的多线程竞争问题,可以通过使用CAS将锁的状态改为REVOKING来避免后一种竞争:通过CAS将锁状态改为REVOKING也可能会有多线线程同时进行,但是CAS保证只有一个线程会成功改变锁状态,称为真正的撤销者(revoker),其他的线程则尝试着获取默认锁(默认锁即偏向锁撤销完成之后变成DEFAULT状态的锁)。

    3 锁操作过程中的场景

    下面介绍偏向锁操作过程(获取、释放、撤销等)的四种主要场景:

    1. 第一种场景是当锁还没有被任何线程获取过处于初始状态NEUTRAL时。在这种状态下,想要获取锁的线程首先会测试锁的状态,然后会使用CAS操作将锁状态中当前持有者线程置为自己的线程ID,这里的CAS操作是必须的,因为需要在多个线程同时获取偏向锁时保证原子性,CAS操作保证了最终只有一个线程会获取到偏向锁,那些获取失败的线程会进入撤销模式,也就是下面会介绍的第3种场景。当一个线程成功的在锁状态中写入自己的线程ID时,即成为该偏向锁的持有者,锁的状态会由NEUTRAL变为BIASED,同时该锁也会被这个线程获取。

    2. 第二种场景是偏向锁的当前持有者线程重新获取锁。在这种场景下,持有者线程只需要检测所的状态位保存自己的线程ID,再测试该锁状态不为REVOKING,即没有被撤销,最后对布尔状态位进行赋值即可。如果该锁被撤销了,锁的当前持有线程的获取动作会失败,需要进入获取默认锁的路径。

    3. 第三种场景发生在第一个非偏向锁持有者尝试获取同一个锁时,通过测试锁当前状态为BIASED,并且锁状态中保存的线程ID不是自己的线程ID可以知道进入此场景。该线程会通过CAS操作将锁的状态由BIASED改为REVOKINGREVOKING是偏向锁撤销为普通锁的中间状态,用来表示锁正在撤销过程中)。因为偏向锁的当前持有线程可能会随时改变锁的状态(比如随时获取、释放偏向锁),所以上面的CAS操作会使用循环进行多次尝试,且每次尝试CAS之前都必须重新读取一下锁状态。需要注意的是,可能会存在多个非持有者线程同时使用CAS尝试将锁状态从BIASED改为REVOKING,CAS则保证最终只有一个线程修改成功,修改成功的线程会变成该锁的撤销者(revoker)。其他修改失败的线程会被通知该锁现在已经不是偏向状态了,从而进入自旋状态直到撤销者成功的完成偏向锁的撤销过程,将锁状态改为DEFAULT,这些线程最终会转向去获取普通锁。偏向锁的撤销者会等待偏向锁的当前持有者释放锁,然后撤销者线程会成功的将锁变成普通锁,然后也会成为该普通锁的第一个获取者。

    4. 第四种场景发生在锁状态为DEFAULT时,进入这个场景有三步:首先,该线程尝试获取初始状态(NEUTRAL)或者偏向锁(BIASED)失败;然后,该线程通过获取普通锁的路径成功获取普通锁;最后,该线程成功获取锁,并从等待锁的阻塞中被唤醒。

    参考文献后面还介绍了可再偏向(rebiasable)锁的实现原理,即在合适的时机再次尝试将锁从DEFALUT状态置为BIASED状态,这里不再介绍,有兴趣可以看看参考文献。

    参考文献

    Dave D., Villiam S.; Quickly Reacquirable Locks

    展开全文
  • 很多人都会称呼它为重量级锁,但是随着Java SE1.6对Synchronized进行了各种优化之后,有些情况下它并不那么重了,本文详细介绍了Java SE1.6中为了减少获得锁和释放锁带来的性能消耗而引入的偏向锁和轻量级锁,...
  • 偏向锁的基本原理

    2021-01-27 14:14:54
    偏向锁的基本原理 前面说过,大部分情况下,锁不仅仅不存在多线程竞争,而是总是由同一个线程多次获得,为了让线程获取锁的代价更低就引入了偏向锁的概念。怎么理解偏向锁呢?当一个线程访问加了同步锁的代码块时,...

    偏向锁的基本原理

    前面说过,大部分情况下,锁不仅仅不存在多线程竞争,而是总是由同一个线程多次获得,为了让线程获取锁的代价更低就引入了偏向锁的概念。怎么理解偏向锁呢?当一个线程访问加了同步锁的代码块时,会在对象头中存储当前线程的 ID后续这个线程进入和退出这段加了同步锁的代码块时,不需要再次加锁和释放锁。而是直接比较对象头里面是否存储了指向当前线程的偏向锁。如果相等表示偏向锁是偏向于当前线程的,就不需要再尝试获得锁了

    为什么要引入偏向锁概念呢

    它通过消除资源无竞争情况下的同步原语,进一步提高了程序的运行性能。

    说白了,就是怕使用者乱用,比方说一个线程的情况下也去竞争锁,避免浪费资源。

    偏向锁的获取和撤销逻辑
    1. 首先获取锁 对象的 Markword,判断是否处于可偏向状态。(biased_lock=1、且 ThreadId 为空)
    2. 如果是可偏向状态,则通过 CAS 操作,把当前线程的 ID 写入到 MarkWord
      a) 如果 cas 成功,那么 markword 就会变成这样。 表示已经获得了锁对象的偏向锁,接着执行同步代码块
      b) 如果 cas 失败,说明有其他线程已经获得了偏向锁,这种情况说明当前锁存在竞争,需要撤销已获得偏向锁的线程,并且把它持有的锁升级为轻量级锁(这个操作需要等到全局安全点,也就是没有线程在执行字节码)才能执行
    3. 如果是已偏向状态,需要检查 markword 中存储的
      ThreadID 是否等于当前线程的 ThreadID
      a) 如果相等,不需要再次获得锁,可直接执行同步代码块
      b) 如果不相等,说明当前锁偏向于其他线程,需要撤销偏向锁并升级到轻量级锁

    偏向锁的撤销

    偏向锁的撤销并不是把对象恢复到无锁可偏向状态(因为偏向锁并不存在锁释放的概念),而是在获取偏向锁的过程中,发现 cas 失败也就是存在线程竞争时,直接把被偏向
    的锁对象升级到被加了轻量级锁的状态。对原持有偏向锁的线程进行撤销时,原获得偏向锁的线程
    有两种情况:

    1. 原获得偏向锁的线程如果已经退出了临界区,也就是同步代码块执行完了,那么这个时候会把对象头设置成无 锁状态并且争抢锁的线程可以基于 CAS 重新偏向当前线程
    2. 如果原获得偏向锁的线程的同步代码块还没执行完,处于临界区之内,这个时候会把原获得偏向锁的线程升级为轻量级锁后继续执行同步代码块
      在我们的应用开发中,绝大部分情况下一定会存在 2 个以上的线程竞争,那么如果开启偏向锁,反而会提升获取锁的资源消耗。所以可以通过 jvm 参数UseBiasedLocking 来设置开启或关闭偏向锁

    流程图分析轻量级锁的基本原理

    在这里插入图片描述

    轻量级锁的加锁和解锁逻辑

    锁升级为轻量级锁之后,对象的 Markword 也会进行相应的的变化。升级为轻量级锁的过程:

    1. 线程在自己的栈桢中创建锁记录 LockRecord。
    2. 将锁对象的对象头中的MarkWord复制到线程的刚刚创
      建的锁记录中。
    3. 将锁记录中的 Owner 指针指向锁对象。
    4. 将锁对象的对象头的MarkWord替换为指向锁记录的指
      针。

    在这里插入图片描述
    在这里插入图片描述

    展开全文
  • 多线程-偏向锁原理

    2021-11-03 11:46:21
    原理: jdk做法是当一个线程第一的到这把锁,...不一致,其他线程占有偏向锁,因为偏向锁不能主动释放,则查看占有偏向锁的线程是否存活 不存活:则直接重置到***无锁状态***,其他线程可以竞争将其设置为偏向锁 ...

    原理:

     jdk做法是当一个线程第一的到这把锁,我们就认为这个锁偏向于它,实际的做法是把线程的放到了对象的对象头里。(偏向锁到轻量级只要由竞争就之间升级)


    当线程访问同步代码块并尝试获取锁,先比较当前线程ID,和偏向锁(是否偏向锁为1)的线程ID是否一致。
    一致,则说明还是线程在获取(重入),无须加锁解锁
    不一致,其他线程占有偏向锁,因为偏向锁不能主动释放,则查看占有偏向锁的线程是否存活
           不存活:则直接重置到***无锁状态***,其他线程可以竞争将其设置为偏向锁
           存活,则等待占有锁的线程进入安全区后暂停,拥有偏向锁的栈会被执行,遍历偏向对象的                     锁记录,栈中的锁记录和对象头的Mark Word,要么重新偏向于其他线程,要么恢复                       到无锁或者标记对象不适合作为偏向锁,最后唤醒暂停的线程


    偏向锁的目标:减少无竞争且只有一个线程使用锁的情况下,使用轻量级锁的性能损耗

    偏向锁 vs 轻量级锁

    轻量级锁每次申请/释放锁都至少需要一次CAS
    偏向锁只有初始化才需要CAS
    缺点:如果明显存在其他线程竞争锁,则很快膨胀成轻量级锁(不过副作用少很多)

    偏向锁的获取

    当一个线程访问同步块并获取锁时,会在对象头和栈帧中的锁记录里存储锁偏向的线程ID,以后该线程在进入和退出同步块时不需要花费CAS操作来加锁和解锁,而只需简单的测试一下对象头的Mark Word里是否存储着指向当前线程的偏向锁,如果测试成功,表示线程已经获得了锁,如果测试失败,则需要再测试下Mark Word中偏向锁的标识是否设置成1(表示当前是偏向锁),如果没有设置,则使用CAS竞争锁,如果设置了,则尝试使用CAS将对象头的偏向锁指向当前线程。

    偏向锁的撤销

    偏向锁使用了一种等到竞争出现才释放锁的机制,所以当其他线程尝试竞争偏向锁时,持有偏向锁的线程才会释放锁。偏向锁的撤销,需要等待全局安全点(在这个时间点上没有字节码正在执行),它会首先暂停拥有偏向锁的线程,然后检查持有偏向锁的线程是否活着,如果线程不处于活动状态,则将对象头设置成无锁状态,如果线程仍然活着,拥有偏向锁的栈会被执行,遍历偏向对象的锁记录,栈中的锁记录和对象头的Mark Word,要么重新偏向于其他线程,要么恢复到无锁或者标记对象不适合作为偏向锁,最后唤醒暂停的线程。

    偏向锁的设置

    关闭偏向锁:偏向锁在Java 6和Java 7里是默认启用的,但是它在应用程序启动几秒钟之后才激活,如有必要可以使用JVM参数来关闭延迟-XX:BiasedLockingStartupDelay = 0。如果你确定自己应用程序里所有的锁通常情况下处于竞争状态,可以通过JVM参数关闭偏向锁-XX:-UseBiasedLocking=false,那么默认会进入轻量级锁状态

    展开全文
  • 如适应性自旋、锁消除、锁粗化、轻量级锁和偏向锁等。(主要指的是synchronized的优化)。 适应性自旋 (自旋锁) 为了让线程等待,我们只需要让线程执行一个忙循环(自旋),这项技术就是所谓的自旋锁。引入自旋锁...

    前言

    为了进一步改进高效并发,HotSpot虚拟机开发团队在JDK1.6版本上花费了大量精力实现各种锁优化。如适应性自旋、锁消除、锁粗化、轻量级锁和偏向锁等。(主要指的是synchronized的优化)。

    适应性自旋 (自旋锁)

    为了让线程等待,我们只需要让线程执行一个忙循环(自旋),这项技术就是所谓的自旋锁。引入自旋锁的原因是互斥同步对性能最大的影响是阻塞的实现,管钱线程和恢复线程的操作都需要转入内核态中完成,给并发带来很大压力。自旋锁让物理机器有一个以上的处理器的时候,能让两个或以上的线程同时并行执行。我们就可以让后面请求锁的那个线程**“稍等一下”**,但不放弃处理器的执行时间,看看持有锁的线程是否很快就会释放锁。为了让线程等待,我们只需让线程执行一个忙循环(自旋),这项技术就是所谓的自旋锁

    自旋锁虽然能避免进入阻塞状态从而减少开销,但是它需要进行忙循环操作占用 CPU 时间,它只适用于共享数据的锁定状态很短的场景

    在 JDK 1.6之前,自旋次数默认是10次,用户可以使用参数-XX:PreBlockSpin来更改。

    JDK1.6引入了自适应的自旋锁。自适应意味着自旋的时间不再固定了,而是由前一次在同一个锁上的自旋时间及锁的拥有者的状态来决定。(这个应该属于试探性的算法)。

    锁消除

    锁消除是指虚拟机即时编译器在运行时,对一些代码上要求同步,但是被检测到不可能存在共享数据竞争的锁进行清除。锁清除的主要判定依据来源于逃逸分析的数据支持,如果判断在一段代码中,堆上的所有数据都不会逃逸出去从而被其他线程访问到,那就可以把它们当做栈上数据对待,认为它们是线程私有的,同步枷锁自然就无需进行

    简单来说,Java 中使用同步 来保证数据的安全性,但是对于一些明显不会产生竞争的情况下,Jvm会根据现实执行情况对代码进行锁消除以提高执行效率。

    举例说明

    对于一些看起来没有加锁的代码,其实隐式的加了很多锁,这些也是锁消除优化的对象。例如下面的字符串拼接代码就隐式加了锁:

    String 是一个不可变的类,编译器会对 String 的拼接自动优化。在 JDK 1.5 之前,会转化为StringBuffer对象的连续 append()操作:

    每个 append() 方法中都有一个同步块。虚拟机观察变量 sb,很快就会发现它的动态作用域被限制在 concatString()方法内部。也就是说,sb 的所有引用永远不会逃逸到concatString()方法之外,其他线程无法访问到它,因此可以进行消除。

    锁粗化

    • 如果一系列的连续操作都对同一个对象反复加锁和解锁,频繁的加锁操作就会导致性能损耗。
    • 当多个彼此靠近的同步块可以合并到一起,形成一个同步块的时候,就会进行锁粗化。该方法还有一种变体,可以把多个同步方法合并为一个方法。如果所有方法都用一个锁对象,就可以尝试这种方法。

    轻量级锁 (@重点知识点)

    JDK 1.6 引入了偏向锁和轻量级锁,从而让锁拥有了四个状态:无锁状态(unlocked)、偏向锁状态(biasble)、轻量级锁状态(lightweight locked)和重量级锁状态(inflated)

    重量级排序 :重量级锁 > 轻量级锁 > 偏向锁 > 无锁

    先介绍一下HotSpot 虚拟机对象头的内存布局:

    上面这些数据被称为Mark Word - 标记关键词。 其中 tag bits 对应了五个状态,这些状态的含义在右侧的 state 表格中给出。除了 marked for gc 状态(gc标记状态),其它四个状态已经在前面介绍过了。

    下图左侧是一个线程的虚拟机栈,其中有一部分称为 Lock Record 的区域,这是在轻量级锁运行过程创建的,用于存放锁对象的 Mark Word。而右侧就是一个锁对象,包含了 Mark Word 和其它信息

    简单来讲,轻量锁就是先通过CAS操作进行同步,因为绝大部分的锁,在整个同步周期都是不存在线程去竞争的。

    获取轻量锁过程当中会当前线程的虚拟机栈中创建一个Lock Record的内存区域去存储获取锁的记录(类似于操作记录?),然后使用CAS操作将锁对象的Mark Word更新成指向刚刚创建的Lock Record的内存区域的指针,如果这个操作成功,就说明线程获取了该对象的锁,把对象的Mark Word 标记00,表示该对象处于轻量级锁状态。失败情况就如上所述,会判断是否是该线程之前已经获取到锁对象了,如果是就进入同步块执行。如果不是,那就是有多个线程竞争这个所对象,那轻量锁就不适用于这个情况了,要膨胀成重量级锁。

    下图是对象处于轻量级锁的状态。

    偏向锁 (@重点知识点)

    偏向锁的思想是偏向于让第一个获取锁对象的线程,这个线程在之后获取该锁就不再需要进行同步操作,甚至连 CAS 操作也不再需要

    当锁对象第一次被线程获得的时候,进入偏向状态,标记为 |1|01|(前面内存布局图中说明了,这属于偏向锁状态)。同时使用 CAS 操作将线程 ID (ThreadID)记录到 Mark Word 中,如果 CAS 操作成功,这个线程以后每次进入这个锁相关的同步块就不需要再进行任何同步操作

    当有另外一个线程去尝试获取这个锁对象时,偏向状态就宣告结束,此时撤销偏向(Revoke Bias)后恢复到未锁定状态或者轻量级锁状态。

    引用《阿里手册:码出高效》的描述再理解一次:

    • 偏向锁是为了在资源没有被多线程竞争的情况下尽量减少锁带来的性能开销。
    • 在锁对象的对象头中有一个ThreadId字段,当第一个线程访问锁时,如果该锁没有被其他线程访问过,即**ThreadId字段为空**,那么JVM让其持有偏向锁,并将ThreadId字段的值设置为该线程的ID。当下一次获取锁的时候,会判断ThreadId是否相等,如果一致就不会重复获取锁,从而提高了运行效率。
    • 如果存在锁的竞争情况,偏向锁就会被撤销并升级为轻量级锁。

    可以结合下面这张锁的状态流转图理解一下:

    上图实际上是摘自《深入理解Java虚拟机》,自己重新画了一次。在画图的过程当中,发现图中有两个点不是很理解,书中也没有对应的解释。就是偏向锁的重偏向撤销偏向时如果判断对象是否已经锁定

    后面经过一段时间的查询才知道,HotSpot支持存储释放偏向锁,以及偏向锁的批量重偏向和撤销。这个特性可以通过JVM的参数进行切换,而且这是默认支持的。

    Unlock状态下Mark Word的一个比特位用于标识该对象偏向锁是否被使用或者是否被禁止。如果该bit位为0,则该对象未被锁定,并且禁止偏向;如果该bit位为1,则意味着该对象处于以下三种状态:

    • 匿名偏向(Anonymously biased)
      在此状态下thread pointerNULL(0),意味着还没有线程偏向于这个锁对象。第一个试图获取该锁的线程将会面临这个情况,使用原子CAS指令可将该锁对象绑定于当前线程。这是允许偏向锁的类对象的初始状态。
    • 可重偏向(Rebiasable)
      在此状态下,偏向锁的epoch字段是无效的(与锁对象对应的class的mark_prototype的epoch值不匹配)。下一个试图获取锁对象的线程将会面临这个情况,使用原子CAS指令可将该锁对象绑定于当前线程**。在批量重偏向的操作中,未被持有的锁对象都被至于这个状态,以便允许被快速重偏向**。
    • 已偏向(Biased)
      这种状态下,thread pointer非空,且epoch为有效值——意味着其他线程正在持有这个锁对象。

    这部分因为我目前暂时不想钻研这么深,就简单描述了一下状态流转机制,就当给自己留个坑先记录一下。想要更深的理解知识的话请需要参考下面的文章(使用关键词"bias revocation"进行搜索观看,第二篇写的很好,之后肯定要全篇好好拜读):

    1. Eliminating Synchronization-Related Atomic Operations with Biased Locking and Bulk Rebiasing (这个是pdf版ppt文件,需要翻墙哦)

    2. Evaluating and improving biased locking in the HotSpot Virtual Machine(这篇我还查到了中文翻译,只不过只翻译了一点,也不保证翻译质量,看原文实际上更好点,讲的很透彻,详细讲了JVM偏向锁的机制,原理,批量重偏向、撤销偏向的操作,相关章节就在下方截图)

    StackOverflow上关于这个议题还有一个很有意思的问题,有兴趣的可以去看看。Does Java ever rebias an individual lock

    通俗点总结

    • 偏向锁是适用于很长一段时间(抽象意义上)都是只有一个线程进入临界区的情况,使用ThreadId进行标记;

    • 轻量锁是适用于会较轻的锁竞争情况,多个线程交替进入临界区,使用CAS获取锁对象;

    • 重量级锁(互斥同步)适用于较严重的锁竞争情况,多个线程同时进入临界区,这个情况下多个线程如果是等待轻量级锁的话,就需要多个线程一直自旋,CPU时间会损失很多;

    参考

    1. 《深入理解Java虚拟机》
    2. 《码出高效》
    3. The Hotspot Java Virtual Machine
    4. Biased Locking in HotSpot
    5. Eliminating Synchronization-Related Atomic Operations with Biased Locking and Bulk Rebiasing
    6. Evaluating and improving biased locking in the HotSpot Virtual Machine
    7. Does Java ever rebias an individual lock
    展开全文
  • Synchronized原理偏向锁篇) 传统的锁机制 传统的锁依赖于系统的同步函数,在linux上使用mutex互斥锁,最底层实现依赖于futex,这些同步函数都涉及到用户态和内核态的切换、进程的上下文切换。这种状态转换需要...
  • java线程安全,优化 互斥同步 互斥是实现同步的手段,临界区、互斥量、信号量都是主要的互斥实现方式。Java中最基本的互斥手段就是synchronized关键字,synchronized关键字在编译后,会在同步块前后分别形成...
  • 偏向锁理解

    千次阅读 2018-04-27 11:51:49
    # markWord: hash码,对象所属的年代,线程持有的对象,状态标志,偏向线程ID,偏向时间等 状态的不同,markword存储的内容也是不同的。(那么读者很有可能会思考一个问题,那原来的数据比如hash码,分代年龄...
  • 锁原理偏向锁、轻量锁、重量锁

    万次阅读 2017-12-18 16:15:16
    java中每个对象都可作为锁,锁有四种级别,按照量级从轻到重分为:无锁、偏向锁、轻量级锁、重量级锁。每个对象一开始都是无锁的,随着线程间争夺锁,越激烈,锁的级别越高,并且锁只能升级不能降级。
  • java对象头是实现synchronized的对象的基础,synchronized使用的对象是存储在Java对象头里的。 对象头包含两部分:Mark Word 和 Class Metadata Address 其中Mark Word在默认情况下存储着对象的HashCode...
  • synchronized原理偏向锁,轻量级锁,重量级锁,锁升级
  • Java多线程优化之偏向锁原理分析

    千次阅读 2015-05-12 18:14:34
    本文来自Ken Wu's Blog,原文标题:《Java偏向锁实现原理(Biased Locking)》 阅读本文的读者,需要对Java轻量级锁有一定的了解,知道lock record, mark word之类的名词。 Java偏向锁(Biased Locking)是Java ...
  • 偏向锁

    万次阅读 多人点赞 2019-04-15 13:07:37
    流程讲解 当JVM启用了偏向锁模式(JDK6以上默认开启),新创建...1.线程A第一次访问同步块时,先检测对象头MarkWord中的标志位是否为01,依此判断此时对象锁是否处于无所状态或者偏向锁状态(匿名偏向锁); 2...
  • 最近因为工作关系遇到了很多Java并发编程的问题,然后恶补了一下,现在就来说说Java目前的锁实现原理 其实在JDK1.5以前的早期版本,还没有...偏向锁和轻量级锁都属于乐观锁,偏向锁指的是没有其他线程竞争资源,只...
  • synchronized实现原理 面试百度的时候,面试官问我synchronzied,尴尬没有看过,不会,哈哈哈,便恶补synchronzied,顺便还有线程池,在我的另外一篇博客里面。 基本上写的博客比较少,很多东西都写的不是很规范,...
  • 很多人都会称呼它为重量级锁,但是随着Java SE1.6对Synchronized进行了各种优化之后,有些情况下它并不那么重了,Java SE1.6中为了减少获得锁和释放锁带来的性能消耗而引入的偏向锁和轻量级锁,以及锁的存储结构和...
  • 无论是对一个对象进行...实际上是加在对象上的,那么被加了的对象我们称之为对象,在java中,任何一个对象都能成为对象。 java对象在内存中的存储结构主要有一下三个部分: 对象头,主要是一些运行时的数据
  • 但是对于锁竞争比较激烈的场合,偏向锁就失效了,因为这样场合极有可能每次申请锁的线程都是不相同的,因此这种场合下不应该使用偏向锁,否则会得不偿失,需要注意的是,偏向锁失败后,并不会立即膨胀为重量级锁,...
  • synchronized原理分析初识 synchronizedsynchronized 用法锁是如何存储的synchronized 锁升级偏向锁偏向锁的获取偏向锁的撤销偏向锁及撤销流程图偏向锁注意事项轻量级锁轻量级锁加锁轻量级锁解锁轻量级锁及膨胀流程...
  • 本文详细介绍了Java偏向锁的实现原理偏向锁是Java 6引入的一项多线程优化。Java多线程优化的实现一般有轻量级锁和偏向锁两种方法。 AD: 本文来自Ken Wu's Blog,原文标题:《Java偏向锁实现...
  • 偏向锁竞争

    2019-12-05 15:24:50
    偏向锁竞争 偏向锁逻辑1.线程A第一次访问同步块时,先检测对象头Mark Word中的标志位是否为01,依此判断此时对象锁是否处于无所状态或者偏向锁状态(匿名偏向锁); 2.然后判断偏向锁标志位是否为1,如果不是,则...
  • 偏向锁状态转移原理

    2018-12-17 12:42:45
    为什么需要偏向锁 当多个处理器同时处理的时候,通常需要处理互斥的问题。 一般的解决方式都会包含acquire和release这个两种操作,操作保证,一个线程在acquire执行之后,在它执行release之前,其它线程不能完成...
  • 可以参考我的一篇博文:Java轻量级锁原理详解(Lightweight Locking) Java偏向锁(Biased Locking)是Java6引入的一项多线程优化。它通过消除资源无竞争情况下的同步原语,进一步提高了程序的运行性能。 轻量级锁也...
  • 锁的状态总共有四种:无锁状态、偏向锁、轻量级锁和重量级锁。随着锁的竞争,锁可以从偏向锁升级到轻量级锁,再升级的重量级锁(但是锁的升级是单向的,也就是说只能从低到高升级,不会出现锁的降级)
  • 的前置知识 如果想要透彻的理解java的来龙去脉,需要先了解锁的基础知识:的类型、java线程阻塞的代价、Markword。 的类型 从宏观上分类,分为悲观与乐观。 乐观 乐观是一种乐观思想,即认为读多...
  • java 偏向锁

    千次阅读 2019-07-23 22:30:52
    文章目录偏向锁1、偏向锁是什么2、优缺点优点缺点3、偏向锁怎么获取前提4、偏向锁的撤销5、可重偏向状态(Rebiasable)6、BiasedLockingBulkRebiasThreshold 参数是干什么用的7、到BiasedLockingBulkRevokeThreshold ...
  • 线程安全 ...它是实现轻量级锁和偏向锁的关键,另一部分用于存储指向方法区对象类型数据的指针,如果是数组则还会有一个额外的部分用于存储数组长度。 考虑到虚拟机的空间效率,Mark Word被设计...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 19,114
精华内容 7,645
关键字:

偏向锁原理