当前位置: 首页 > news >正文

长春网站建设 4435seo提升排名

长春网站建设 4435,seo提升排名,东营新闻联播在线直播今晚,wordpress 短链插件本文介绍为了实现高效并发#xff0c;虚拟机对 synchronized 做的一系列的锁优化措施 高效并发是从 JDK5 升级到 JDK6 后一项重要的改进项#xff0c;HotSpot 虚拟机开发团队在 JDK6 这个版本上花费了大量的资源去实现各种锁优化技术#xff0c;如适应性自旋#xff08;Ada… 本文介绍为了实现高效并发虚拟机对 synchronized 做的一系列的锁优化措施 高效并发是从 JDK5 升级到 JDK6 后一项重要的改进项HotSpot 虚拟机开发团队在 JDK6 这个版本上花费了大量的资源去实现各种锁优化技术如适应性自旋Adaptive Spinning、锁消除Lock Elimination、锁膨胀Lock Coarsening、 轻量级锁Lightweight Locking 、偏向锁Biased Locking等这些技术都是为了在线程之间更高效地共享数据及解决竞争问题从而提高程序的执行效率。 自旋锁 自适应自旋 在许多应用上共享数据的锁定状态只会持续很短的一段时间为了这段时间去挂起和恢复线程并不值得。 自旋锁指的是线程 A 成功获取锁后线程 B 请求锁时请求锁的线程 B 执行一个忙循环自旋不放弃处理器的执行时间看看持有锁的线程 A 是否会很快就释放锁。自旋等待的时间有一定的限度如果自旋超过了限定的次数仍然没有成功获得锁就应当使用传统的方式去挂起线程。 自适应自旋指的是自旋的时间不再是固定的了而是由前一次在同一个锁上的自旋时间及锁的拥有者的状态来决定的。 前面我们讨论互斥同步的时候提到了互斥同步对性能影响最大的是阻塞的实现挂起线程和恢复线程的操作都需要转入内核态中完成这些操作给Java虚拟机的并发性能带来了很大的压力。同时虚拟机的开发团队也注意到在许多应用上共享数据的锁定状态只会持续很短的一段时间为了这段时间去挂起和恢复线程并不值得。现在绝大多数的个人电脑和服务器都是多路核处理器系统如果物理机器有一个以上的处理器或者处理器核心能让两个或以上的线程同时并行执行我们就可以让后面请求锁的那个线程“稍等一会”但不放弃处理器的执行时间看看持有锁的线程是否很快就会释放锁。为了让线程等待我们只须让线程执行一个忙循环自旋这项技术就是所谓的自旋锁。 自旋锁在 JDK1.4.2 中就已经引入只不过默认是关闭的可以使用 -XXUseSpinning 参数来开启在 JDK6 中就已经改为默认开启了。自旋等待不能代替阻塞且先不说对处理器数量的要求自旋等待本身虽然避免了线程切换的开销但它是要占用处理器时间的所以如果锁被占用的时间很短自旋等待的效果就会非常好反之如果锁被占用的时间很长 那么自旋的线程只会白白消耗处理器资源而不会做任何有价值的工作这就会带来性能的浪费。因此自旋等待的时间必须有一定的限度如果自旋超过了限定的次数仍然没有成功获得锁就应当使用传统的方式去挂起线程。自旋次数的默认值是十次用户也可以使用参数 -XXPreBlockSpin 来自行更改。 不过无论是默认值还是用户指定的自旋次数对整个 Java 虚拟机中所有的锁来说都是相同的。在 JDK6 中对自旋锁的优化引入了自适应的自旋。自适应意味着自旋的时间不再是固定的了而是由前一次在同一个锁上的自旋时间及锁的拥有者的状态来决定的。 如果在同一个锁对象上自旋等待刚刚成功获得过锁 并且持有锁的线程正在运行中那么虚拟机就会认为这次自旋也很有可能再次成功进而允许自旋等待持续相对更长的时间比如持续100次忙循环。另一方面如果对于某个锁自旋很少成功获得过锁那在以后要获取这个锁时将有可能直接省略掉自旋过程以避免浪费处理器资源。 有了自适应自旋随着程序运行时间的增长及性能监控信息的不断完善虚拟机对程序锁的状况预测就会越来越精准虚拟机就会变得越来越“聪明”了。 锁消除 锁消除是指虚拟机即时编译器在运行时对一些代码要求同步但是被检测到不可能存在共享数据竞争的锁进行消除。锁消除的主要判定依据来源于逃逸分析的数据支持如果判断到一段代码中在堆上的所有数据都不会逃逸出去被其他线程访问到那就可以把它们当作栈上数据对待认为它们是线程私有的同步加锁自然就无须再进行。 也许读者会有疑问变量是否逃逸对于虚拟机来说是需要使用复杂的过程间分析才能确定的但是程序员自己应该是很清楚的怎么会在明知道不存在数据争用的情况下还要求同步呢这个问题的答案是有许多同步措施并不是程序员自己加入的同步的代码在 Java 程序中出现的频繁程度也许超过了大部分读者的想象。我们来看看如代码清单13-6所示的例子这段非常简单的代码仅仅是输出三个字符串相加的结果无论是源代码字面上 还是程序语义上都没有进行同步。 // 代码清单13-6 一段看起来没有同步的代码 public String concatString(String s1, String s2, String s3) {return s1 s2 s3; }我们也知道由于 String 是一个不可变的类对字符串的连接操作总是通过生成新的 String 对象来进行的因此 Javac 编译器会对 String 连接做自动优化。 在 JDK5 之前字符串加法会转化为 StringBuffer 对象的连续 append() 操作。即代码清单13-6所示的代码可能会变成代码清单13-7所示的样子。在 JDK5 及以后的版本中会转化为 StringBuilder 对象的连续 append() 操作。 // 代码清单13-7 Javac转化后的字符串连接操作 public String concatString(String s1, String s2, String s3) {StringBuffer sb new StringBuffer();sb.append(s1);sb.append(s2);sb.append(s3);return sb.toString(); }现在大家还认为这段代码没有涉及同步吗每个 StringBuffer.append() 方法中都有一个同步块锁就是 sb 对象。虚拟机观察 sb 变量经过逃逸分析后会发现它的动态作用域被限制在 concatString() 方法内部。也就是 sb 的所有引用都永远不会逃逸到 concatString() 方法之外其他线程无法访问到它所以这里虽然有锁但是可以被安全地消除掉。在解释执行时这里仍然会加锁但在经过服务端编译器的即时编译之后这段代码就会忽略所有的同步措施而直接执行。 锁粗化 锁粗化指的是如果虚拟机探测到有一串零碎的操作都对同一个对象加锁那么虚拟机将会把加锁同步的范围扩展粗化到整个操作序列的外部。 原则上我们在编写代码的时候总是推荐将同步块的作用范围限制得尽量小只在共享数据的实际作用域中才进行同步这样是为了使得需要同步的操作数量尽可能变少即使存在锁竞争等待锁的线程也能尽可能快地拿到锁。 大多数情况下上面的原则都是正确的但是如果一系列的连续操作都对同一个对象反复加锁和解锁甚至加锁操作是出现在循环体之中的那即使没有线程竞争频繁地进行互斥同步操作也会导致不必要的性能损耗。 代码清单13-7所示连续的 append() 方法就属于这类情况。如果虚拟机探测到有这样一串零碎的操作都对同一个对象加锁将会把加锁同步的范围扩展粗化到整个操作序列的外部以代码清单13-7为例就是扩展到第一个 append() 操作之前直至最后一个 append() 操作之后这样只需要加锁一次就可以了。 轻量级锁 轻量级锁的设计初衷是在没有多线程竞争的情况下通过使用 CASCompare And Swap操作来进行线程同步减少传统的重量级锁使用操作系统互斥量产生的性能消耗。 轻量级锁可以提高带有同步但无竞争的程序性能但它是一个带有效益权衡Trade Off 性质的优化也就是说它并非总是对程序运行有利。轻量级锁能提升程序同步性能的依据是 “对于绝大部分的锁在整个同步周期内都是不存在竞争的” 这一经验法则。 如果没有竞争轻量级锁便通过 CAS 操作成功避免了使用互斥量的开销但如果确实存在锁竞争除了互斥量的本身开销外还额外发生了 CAS 操作的开销。 因此在有竞争的情况下轻量级锁反而会比传统的重量级锁更慢。 轻量级锁是 JDK6 时加入的新型锁机制它名字中的 “轻量级” 是相对于使用操作系统互斥量来实现的传统锁而言的 因此传统的锁机制就被称为“重量级”锁。不过需要强调一点轻量级锁并不是用来代替重量级锁的轻量级锁设计的初衷是在没有多线程竞争的前提下减少传统的重量级锁使用操作系统互斥量产生的性能消耗。 Mark Word 要理解轻量级锁以及后面会讲到的偏向锁的原理和运作过程必须要对 HotSpot 虚拟机对象的内存布局尤其是对象头部分有所了解。HotSpot 虚拟机的对象头Object Header分为两部分 第一部分用于存储对象自身的运行时数据如哈希码HashCode、GC 分代年龄Generational GC Age等。这部分数据的长度在 32 位和 64 位的 Java 虚拟机中分别会占用 32 个或 64 个比特官方称它为 “Mark Word”。这部分是实现轻量级锁和偏向锁的关键。另外一部分用于存储指向方法区对象类型数据的指针Class Pointer、类型指针虚拟机通过这个指针来确定这个对象是哪个类的实例。如果是数组对象还会有一个额外的部分用于存储数组长度。 由于对象头信息是与对象自身定义的数据无关的额外存储成本考虑到 Java 虚拟机的空间使用效率Mark Word 被设计成一个非固定的动态数据结构以便在极小的空间内存储尽量多的信息。它会根据对象的状态复用自己的存储空间。例如在 32 位的 HotSpot 虚拟机中 对象未被锁定的状态下Mark Word 的 32 个比特空间里的 25 个比特将用于存储对象哈希码4 个比特用于存储对象分代年龄2 个比特用于存储锁标志位还有 1 个比特固定为 0这表示未进入偏向模式。对象除了未被锁定的正常状态外还有轻量级锁定、重量级锁定、GC 标记、可偏向等几种不同状态这些状态下对象头的存储内容如下表所示。 工作过程 我们简单回顾了对象的内存布局后接下来就可以介绍轻量级锁的工作过程了在代码即将进入同步块的时候如果此同步对象没有被锁定锁标志位为“01”状态虚拟机首先将在当前线程的栈帧中建立一个名为锁记录Lock Record的空间 用于存储锁对象目前的 Mark Word 的拷贝官方为这份拷贝加了一个 Displaced 前缀即 Displaced Mark Word这时候线程堆栈与对象头的状态如图13-3所示。 图13-3轻量级锁 CAS 操作之前堆栈与对象的状态 然后 虚拟机将使用 CAS 操作尝试把对象的 Mark Word 更新为指向锁记录Lock Record的指针。 如果这个更新操作成功了即代表该线程拥有了这个对象的锁并且对象 Mark Word 的锁标志位Mark Word 的最后两个比特将转变为 “00”表示此对象处于轻量级锁定状态。这时候线程堆栈与对象头的状态如图13-4所示。如果这个更新操作失败了那就意味着至少存在一条线程与当前线程竞争获取该对象的锁。虚拟机首先会检查对象的 Mark Word 是否指向当前线程的栈帧如果是说明当前线程已经拥有了这个对象的锁那直接进入同步块继续执行就可以了否则对象的 Mark Word 不是指向当前线程的栈帧就说明这个锁对象已经被其他线程抢占了。如果出现两条以上的线程争用同一个锁的情况那轻量级锁就不再有效必须要膨胀为重量级锁锁标志的状态值变为“10”此时 Mark Word 中存储的就是指向重量级锁互斥量的指针后面等待锁的线程也必须进入阻塞状态。 图13-4轻量级锁 CAS 操作之后堆栈与对象的状态 上面描述的是轻量级锁的加锁过程它的解锁过程也同样是通过 CAS 操作来进行的如果对象的 Mark Word 仍然指向线程的锁记录那就用 CAS 操作把对象当前的 Mark Word 和线程中复制的 Displaced Mark Word 替换回来。 假如能够替换成功那整个同步过程就顺利完成了如果替换失败则说明有其他线程尝试过获取该锁就要在释放锁的同时唤醒被挂起的线程。 轻量级锁能提升程序同步性能的依据是 “对于绝大部分的锁在整个同步周期内都是不存在竞争的” 这一经验法则。 如果没有竞争轻量级锁便通过 CAS 操作成功避免了使用互斥量的开销但如果确实存在锁竞争除了互斥量的本身开销外还额外发生了 CAS 操作的开销。 因此在有竞争的情况下轻量级锁反而会比传统的重量级锁更慢。 偏向锁 偏向锁的目的是消除数据在无竞争情况下的同步原语进一步提高程序的运行性能。 偏向锁中的“偏”的意思是这个锁会偏向于第一个获得它的线程。如果虚拟机启用了偏向锁那么当锁对象第一次被线程获取的时候虚拟机将会把对象头中的标志位设置为 “01”、把偏向模式设置为 “1”表示进入偏向模式。同时使用 CAS 操作把获取到这个锁的线程的 ID 记录在对象的 Mark Word 之中。如果 CAS 操作成功持有偏向锁的线程以后每次进入这个锁相关的同步块时虚拟机都可以不再进行任何同步操作例如加锁、解锁及对 Mark Word 的更新操作等。 偏向锁可以提高带有同步但无竞争的程序性能但它同样是一个带有效益权衡Trade Off 性质的优化也就是说它并非总是对程序运行有利。如果程序中大多数的锁都总是被多个不同的线程访问那偏向模式就是多余的。 偏向锁也是 JDK6 中引入的一项锁优化措施它的目的是消除数据在无竞争情况下的同步原语进一步提高程序的运行性能。如果说轻量级锁是在无竞争的情况下使用 CAS 操作去消除同步使用的互斥量那偏向锁就是在无竞争的情况下把整个同步都消除掉连 CAS 操作都不去做了。 偏向锁中的“偏”就是偏心的“偏”、偏袒的“偏”。偏向锁中的“偏”的意思是这个锁会偏向于第一个获得它的线程如果在接下来的执行过程中该锁一直没有被其他的线程获取则持有偏向锁的线程将永远不需要再进行同步。 如果读者理解了前面轻量级锁中关于对象头 Mark Word 与线程之间的操作过程那偏向锁的原理就会很容易理解。 假设当前虚拟机启用了偏向锁启用参数 -XXUseBiased Locking这是自 JDK6 起 HotSpot 虚拟机的默认值那么当锁对象第一次被线程获取的时候虚拟机将会把对象头中的标志位设置为 “01”、把偏向模式设置为 “1”表示进入偏向模式。同时使用 CAS 操作把获取到这个锁的线程的 ID 记录在对象的 Mark Word 之中。如果 CAS 操作成功持有偏向锁的线程以后每次进入这个锁相关的同步块时虚拟机都可以不再进行任何同步操作例如加锁、解锁及对 Mark Word 的更新操作等。 一旦出现另外一个线程去尝试获取这个锁的情况偏向模式就马上宣告结束。根据锁对象目前是否处于被锁定的状态决定是否撤销偏向偏向模式设置为 “0”撤销后标志位恢复到未锁定标志位为 “01”或轻量级锁定标志位为 “00”的状态后续的同步操作就按照上面介绍的轻量级锁那样去执行。 偏向锁、轻量级锁的状态转换及对象 Mark Word 的关系如图13-5所示。 图13-5偏向锁、轻量级锁的状态转换及及对象 Mark Word 的关系 细心的读者看到这里可能会发现一个问题当对象进入偏向状态的时候Mark Word 大部分的空间23个比特 都用于存储持有锁的线程 ID 了这部分空间占用了原有存储对象哈希码的位置那原来对象的哈希码怎么办呢 在 Java 语言里面一个对象如果计算过哈希码就应该一直保持该值不变强烈推荐但不强制因为用户可以重载hashCode() 方法按自己的意愿返回哈希码否则很多依赖对象哈希码的 API 都可能存在出错风险。而作为绝大多数对象哈希码来源的 Object::hashCode() 方法返回的是对象的一致性哈希码Identity Hash Code这个值是能强制保证不变的它通过在对象头中存储计算结果来保证第一次计算之后再次调用该方法取到的哈希码值永远不会再发生改变。 因此当一个对象已经计算过一致性哈希码后它就再也无法进入偏向锁状态了而当一个对象当前正处于偏向锁状态 又收到需要计算其一致性哈希码请求时它的偏向状态会被立即撤销并且锁会膨胀为重量级锁。在重量级锁的实现中 对象头指向了重量级锁的位置代表重量级锁的 ObjectMonitor 类里有字段可以记录非加锁状态标志位为“01”下的Mark Word其中自然可以存储原来的哈希码。 注意 这里说的计算请求应来自于对Object::hashCode()或者System::identityHashCode(Object)方法的调用 如果重写了对象的hashCode()方法 计算哈希码时并不会产生这里所说的请求。 偏向锁可以提高带有同步但无竞争的程序性能但它同样是一个带有效益权衡Trade Off 性质的优化也就是说它并非总是对程序运行有利。如果程序中大多数的锁都总是被多个不同的线程访问那偏向模式就是多余的。在具体问题具体分析的前提下有时候使用参数-XX-UseBiasedLocking 来禁止偏向锁优化反而可以提升性能。 完整的过程 假设当前虚拟机启用了偏向锁那么当锁对象第一次被线程获取的时候虚拟机将会把对象头中的标志位设置为 “01”、把偏向模式设置为 “1”表示进入偏向模式。同时使用 CAS 操作把获取到这个锁的线程的 ID 记录在对象的 Mark Word 之中。如果 CAS 操作成功持有偏向锁的线程以后每次进入这个锁相关的同步块时虚拟机都可以不再进行任何同步操作例如加锁、解锁及对 Mark Word 的更新操作等。 如果锁对象目前处于偏向模式那么一旦出现另外一个线程去尝试获取这个锁的情况偏向模式就马上宣告结束。根据锁对象目前是否处于被锁定的状态决定撤销偏向后锁对象处于什么状态。 如果锁对象目前处于被锁定的状态那么一旦出现另外一个线程去尝试获取这个锁的情况偏向模式就马上宣告结束锁对象转换到轻量级锁定状态后续的同步操作就按照轻量级锁那样去执行。如果锁对象目前处于未被锁定的状态那么一旦出现另外一个线程去尝试获取这个锁的情况偏向模式就马上宣告结束锁对象转换到未被锁定、不可偏向状态。 对象转换到轻量级锁定状态。虚拟机首先将在当前线程的栈帧中建立一个名为锁记录Lock Record的空间用于存储锁对象目前的 Mark Word 的拷贝。然后虚拟机将使用 CAS 操作尝试把对象的 Mark Word 更新为指向锁记录Lock Record的指针。 如果这个更新操作成功了即代表该线程拥有了这个对象的锁并且对象 Mark Word 的锁标志位将转变为 “00”表示此对象处于轻量级锁定状态。如果这个更新操作失败了那就意味着至少存在一条线程与当前线程竞争获取该对象的锁。虚拟机首先会检查对象的 Mark Word 是否指向当前线程的栈帧 如果是对象的 Mark Word 指向当前线程的栈帧说明当前线程已经拥有了这个对象的锁那直接进入同步块继续执行就可以了否则对象的 Mark Word 不是指向当前线程的栈帧就说明这个锁对象已经被其他线程抢占了那么当前线程 B 执行一个忙循环自旋不放弃处理器的执行时间看看持有锁的线程 A 是否会很快就释放锁。 如果持有锁的线程 A 很快就释放了锁那么当前线程 B 成功获取锁。如果线程 B 自旋超过了限定的次数仍然没有成功获得锁那轻量级锁就不再有效必须要膨胀为重量级锁锁标志的状态值变为“10”此时 Mark Word 中存储的就是指向重量级锁互斥量的指针。当前线程继续等待锁并进入阻塞状态。持有锁的线程 A 释放锁的同时唤醒被挂起的线程。被唤醒的线程就会进行新一轮的竞争尝试获取这个锁。 参考资料 第13章 线程安全与锁优化 13.3 锁优化
http://www.hkea.cn/news/14398841/

相关文章:

  • wap网站的未来赣州人才网招聘
  • 网站设计制作有哪些原因常州高端网站制作公司排名
  • wordpress标题调用企业网站seo外包
  • 黄江镇网站建设公司提交网站入口
  • 可信网站认证服务商营销型网站与展示型网站
  • 域名如何解析别人网站潍坊网站制作网络科技
  • 广州企业建站 网络服务江门网站推广公司
  • 做马来西亚生意的网站淘宝代运营是什么意思
  • vs网站界面是什么做的国内网站绕过备案方法
  • 人才网站的seo怎么做有关建设网站的问题
  • 制作学校网站网站建设款分录
  • 网站建设管理情况说明哈尔滨门户网站是什么
  • 在哪家网站上可以找到加工活做分类信息网址导航
  • 中小企业网站设计与开发目的包头建设厅官方网站
  • 免费的空间网站四川煤矿标准化建设网站
  • 如何提交网站连接到百度潍坊信息网网站建设
  • 网站开发费用投入情况深圳网站设计 公司
  • 单页网站开发移动网站怎么做
  • 聊城网站建设价位dw做的网站有缝怎么办
  • 中国城乡建中国城乡建设部网站域名和服务器多少钱
  • 网站权限怎么设置方法网上营销推广
  • 网站设计公司产品介绍做网咖的网站
  • 汝州网站建设朱腾鹏个人网站
  • 节点网站python创建wordpress
  • 单位网站建设建议对策国内互联网推广
  • 云南定制化网站建设河南省住房城乡建设厅官网
  • 西安网站设计哪家好寺院的网站怎么做
  • 本校网站建设域名企业备案对网站的好处
  • 个人艺术作品网站建设策划书民政局两学一做专题网站
  • 企业名录网站 优帮云汽车网络营销策划方案