手机网站好处,需要做网站的企业电话,做亳州旅游网站的目的,中企动力科技股份有限公司重庆分公司偏向锁
偏向锁是JDK6中的重要引进#xff0c;因为HotSpot作者经过研究实践发现#xff0c;在大多数情况下#xff0c;锁不仅不存在多线程竞争#xff0c;而且总是由同一线程多次获得#xff0c;为了让线程获得锁的代价更低#xff0c;引进了偏向锁。
偏向锁是在单线程执…偏向锁
偏向锁是JDK6中的重要引进因为HotSpot作者经过研究实践发现在大多数情况下锁不仅不存在多线程竞争而且总是由同一线程多次获得为了让线程获得锁的代价更低引进了偏向锁。
偏向锁是在单线程执行代码块时使用的机制如果在多线程并发的环境下即线程A尚未执行完同步代码块线程B发起了申请锁的申请则一定会转化为轻量级锁或者重量级锁。
在JDK5中偏向锁默认是关闭的而到了JDK6中偏向锁已经默认开启。如果并发数较大同时同步代码块执行时间较长则被多个线程同时访问的概率就很大就可以使用参数-XX:-UseBiasedLocking来禁止偏向锁(但这是个JVM参数不能针对某个对象锁来单独设置)。
引入偏向锁主要目的是为了在没有多线程竞争的情况下尽量减少不必要的轻量级锁执行路径。因为轻量级锁的加锁解锁操作是需要依赖多次CAS原子指令的而偏向锁只需要在置换ThreadID的时候依赖一次CAS原子指令由于一旦出现多线程竞争的情况就必须撤销偏向锁所以偏向锁的撤销操作的性能损耗也必须小于节省下来的CAS原子指令的性能消耗。 轻量级锁是为了在线程交替执行同步块时提高性能而偏向锁则是在只有一个线程执行同步块时进一步提高性能。 那么偏向锁是如何来减少不必要的CAS操作呢首先我们看下无竞争下锁存在什么问题 现在几乎所有的锁都是可重入的即已经获得锁的线程可以多次锁住/解锁监视对象按照之前的HotSpot设计每次加锁/解锁都会涉及到一些CAS操作比如对等待队列的CAS操作CAS操作会延迟本地调用因此偏向锁的想法是 一旦线程第一次获得了监视对象之后让监视对象“偏向”这个线程之后的多次调用则可以避免CAS操作说白了就是置个变量如果发现为true则无需再走各种加锁/解锁流程。 CAS为什么会引入本地延迟这要从SMP对称多处理器架构说起下图大概表明了SMP的结构 SMP对称多处理器架构 其意思是 所有的CPU会共享一条系统总线BUS靠此总线连接主存。每个核都有自己的一级缓存各核相对于BUS对称分布因此这种结构称为“对称多处理器”。 而CAS的全称为Compare-And-Swap是一条CPU的原子指令其作用是让CPU比较后原子地更新某个位置的值经过调查发现其实现方式是基于硬件平台的汇编指令就是说CAS是靠硬件实现的JVM只是封装了汇编调用那些AtomicInteger类便是使用了这些封装后的接口。
例如Core1和Core2可能会同时把主存中某个位置的值Load到自己的L1 Cache中当Core1在自己的L1 Cache中修改这个位置的值时会通过总线使Core2中L1 Cache对应的值“失效”而Core2一旦发现自己L1 Cache中的值失效称为Cache命中缺失则会通过总线从内存中加载该地址最新的值大家通过总线的来回通信称为“Cache一致性流量”因为总线被设计为固定的“通信能力”如果Cache一致性流量过大总线将成为瓶颈。而当Core1和Core2中的值再次一致时称为“Cache一致性”从这个层面来说锁设计的终极目标便是减少Cache一致性流量。
而CAS恰好会导致Cache一致性流量如果有很多线程都共享同一个对象当某个Core CAS成功时必然会引起总线风暴这就是所谓的本地延迟本质上偏向锁就是为了消除CAS降低Cache一致性流量。
Cache一致性 上面提到Cache一致性其实是有协议支持的现在通用的协议是MESI最早由Intel开始支持具体参考http://en.wikipedia.org/wiki/MESI_protocol。 Cache一致性流量的例外情况 其实也不是所有的CAS都会导致总线风暴这跟Cache一致性协议有关具体参考http://blogs.oracle.com/dave/entry/biased_locking_in_hotspot NUMA(Non Uniform Memory Access Achitecture架构 与SMP对应还有非对称多处理器架构现在主要应用在一些高端处理器上主要特点是没有总线没有公用主存每个Core有自己的内存针对这种结构此处不做讨论。 所以当一个线程访问同步块并获取锁时会在对象头和栈帧中的锁记录里存储锁偏向的线程ID以后该线程进入和退出同步块时不需要花费CAS操作来争夺锁资源只需要检查是否为偏向锁、锁标识为以及ThreadID即可处理流程如下 检测Mark Word是否为可偏向状态即是否为偏向锁1锁标识位为01若为可偏向状态则测试线程ID是否为当前线程ID如果是则执行步骤5否则执行步骤3如果测试线程ID不为当前线程ID则通过CAS操作竞争锁竞争成功则将Mark Word的线程ID替换为当前线程ID否则执行线程4通过CAS竞争锁失败证明当前存在多线程竞争情况当到达全局安全点获得偏向锁的线程被挂起偏向锁升级为轻量级锁然后被阻塞在安全点的线程继续往下执行同步代码块执行同步代码块 偏向锁的释放采用了 一种只有竞争才会释放锁的机制线程是不会主动去释放偏向锁需要等待其他线程来竞争。偏向锁的撤销需要 等待全局安全点这个时间点上是没有正在执行的代码。其步骤如下 暂停拥有偏向锁的线程判断锁对象是否还处于被锁定状态否则恢复到无锁状态01以允许其余线程竞争。是则挂起持有锁的当前线程并将指向当前线程的锁记录地址的指针放入对象头Mark Word升级为轻量级锁状态00然后恢复持有锁的当前线程进入轻量级锁的竞争模式 注意此处将 当前线程挂起再恢复的过程中并没有发生锁的转移仍然在当前线程手中只是穿插了个 “将对象头中的线程ID变更为指向锁记录地址的指针” 这么个事。 偏向锁的获取和释放过程 轻量级锁
引入轻量级锁的主要目的是 在没有多线程竞争的前提下减少传统的重量级锁使用操作系统互斥量产生的性能消耗。当关闭偏向锁功能或者多个线程竞争偏向锁导致偏向锁升级为轻量级锁则会尝试获取轻量级锁其步骤如下
在线程进入同步块时如果同步对象锁状态为无锁状态锁标志位为“01”状态是否为偏向锁为“0”虚拟机首先将在当前线程的栈帧中建立一个名为锁记录Lock Record的空间用于存储锁对象目前的Mark Word的拷贝官方称之为 Displaced Mark Word。此时线程堆栈与对象头的状态如下图所示 轻量级锁CAS操作之前线程堆栈与对象的状态 拷贝对象头中的Mark Word复制到锁记录Lock Record中 拷贝成功后虚拟机将使用CAS操作尝试将对象Mark Word中的Lock Word更新为指向当前线程Lock Record的指针并将Lock record里的owner指针指向object mark word。Lock Word属于线程对象中的Lock Word是指向线程Lock Word的指针线程Lock Word 记录对象的Object mark word 相当于 对象和线程相互关联 如果这个更新动作成功了那么当前线程就拥有了该对象的锁并且对象Mark Word的锁标志位设置为“00”即表示此对象处于轻量级锁定状态此时线程堆栈与对象头的状态如下图所示 轻量级锁CAS操作之后线程堆栈与对象的状态 如果这个更新操作失败了虚拟机首先会检查对象Mark Word中的Lock Word是否指向当前线程的栈帧如果是就说明当前线程已经拥有了这个对象的锁那就可以直接进入同步块继续执行。否则说明多个线程竞争锁进入自旋执行若自旋结束时仍未获得锁轻量级锁就要膨胀为重量级锁锁标志的状态值变为“10”Mark Word中存储的就是指向重量级锁互斥量的指针当前线程以及后面等待锁的线程也要进入阻塞状态。
轻量级锁的释放也是通过CAS操作来进行的主要步骤如下 通过CAS操作尝试把线程中复制的Displaced Mark Word对象替换当前的Mark Word如果替换成功整个同步过程就完成了恢复到无锁状态01如果替换失败说明有其他线程尝试过获取该锁此时锁已膨胀那就要在释放锁的同时唤醒被挂起的线程 对于轻量级锁其性能提升的依据是 “对于绝大部分的锁在整个生命周期内都是不会存在竞争的”如果打破这个依据则除了互斥的开销外还有额外的CAS操作因此在有多线程竞争的情况下轻量级锁比重量级锁更慢。 轻量级锁的获取和释放过程 为什么升级为轻量锁时要把对象头里的Mark Word复制到线程栈的锁记录中呢 因为在申请对象锁时 需要以该值作为CAS的比较条件同时在升级到重量级锁的时候能通过这个比较判定是否在持有锁的过程中此锁被其他线程申请过如果被其他线程申请了则在释放锁的时候要唤醒被挂起的线程。 为什么会尝试CAS不成功以及什么情况下会不成功 CAS本身是不带锁机制的其是通过比较而来。假设如下场景线程A和线程B都在对象头里的锁标识为无锁状态进入那么如线程A先更新对象头为其锁记录指针成功之后线程B再用CAS去更新就会发现此时的对象头已经不是其操作前的对象HashCode了所以CAS会失败。也就是说只有两个线程并发申请锁的时候会发生CAS失败。 然后线程B进行CAS自旋等待对象头的锁标识重新变回无锁状态或对象头内容等于对象HashCode因为这是线程B做CAS操作前的值这也就意味着线程A执行结束参见后面轻量级锁的撤销只有线程A执行完毕撤销锁了才会重置对象头此时线程B的CAS操作终于成功了于是线程B获得了锁以及执行同步代码的权限。如果线程A的执行时间较长线程B经过若干次CAS时钟没有成功则锁膨胀为重量级锁即线程B被挂起阻塞、等待重新调度。
此处如何理解“轻量级”“轻量级”是相对于使用操作系统互斥量来实现的传统锁而言的。但是首先需要强调一点的是轻量级锁并不是用来代替重量级锁的它的本意是在没有多线程竞争的前提下减少传统的重量级锁使用产生的性能消耗。 轻量级锁所适应的场景是线程交替执行同步块的情况如果存在同一时间访问同一锁的情况必然就会导致轻量级锁膨胀为重量级锁。 重量级锁
Synchronized是通过对象内部的一个叫做 监视器锁Monitor来实现的。但是监视器锁本质又是依赖于底层的操作系统的Mutex Lock来实现的。而操作系统实现线程之间的切换这就需要从用户态转换到核心态这个成本非常高状态之间的转换需要相对比较长的时间这就是为什么Synchronized效率低的原因。因此这种依赖于操作系统Mutex Lock所实现的锁我们称之为 “重量级锁”。
重量级锁、轻量级锁和偏向锁之间转换 重量级锁、轻量级锁和偏向锁之间转换 Synchronized偏向锁、轻量级锁及重量级锁转换流程
知识来源
https://www.cnblogs.com/aspirant/p/11470858.html