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

团购网站建设2021近期时事新闻热点事件

团购网站建设,2021近期时事新闻热点事件,做网站好的,青岛网站建设公司排名创作内容丰富的干货文章很费心力,感谢点过此文章的读者,点一个关注鼓励一下作者,激励他分享更多的精彩好文,谢谢大家! 双重锁定检查(Double Checked Locking,下称 DCL)是并发下实现懒…

创作内容丰富的干货文章很费心力,感谢点过此文章的读者,点一个关注鼓励一下作者,激励他分享更多的精彩好文,谢谢大家!


双重锁定检查(Double Checked Locking,下称 DCL)是并发下实现懒加载的一个模式,在实现单例模式时很常见,但是要正确实现 DCL,其中涉及到的细节和知识是非常琐碎的,我们这里按照 The "Double-Checked Locking is Broken" Declaration 文章的脉络,结合前几章学习的知识,尝试理解这些知识点。

(这章属于“骚操作”的内容。)

初次尝试

上节中说过 Lazy Initialization,我们的目标是在获取某个实例时只初始化一次,在单线程语境中,我们会这么实现:

class Foo {private Helper helper = null;public Helper getHelper() {if (helper == null)helper = new Helper();return helper;}// other functions and members...
}

但是我们知道这个版本在多线程下是有问题的,因为对 helper 和检查和赋值不是原子的,有可能多个线程同时满足了 if (helper == null) 的判断,最终多个线程都执行了 helper = new Helper 的操作。一个简单的方法是加锁:

class Foo {private Helper helper = null;public synchronized Helper getHelper() {if (helper == null)helper = new Helper();return helper;}// other functions and members...
}

注意代码里的 synchronized。这个代码能正确运行,但是效率低下,因为 synchronized 是互斥锁,后续所有 getHelper 调用都得加锁。于是我们希望在 helper 正确初始化后就不再加锁了,尝试如下实现:

class Foo {private Helper helper = null;public synchronized Helper getHelper() {if (helper == null)             // ① 第一次检查synchronized(this) {        // ② 对 helper 加锁if (helper == null)         // ③ 同上个实现helper = new Helper();}return helper;}// other functions and members...
}

代码的初衷是:

  1. 如果正确初始化后,所有的 getHelper ① 的条件失败,于是不需要synchronized
  2. 如果未被正确初始化,则同上个实现一样,加锁进行初始化。

Unfortunately, that code just does not work in the presence of either optimizing compilers or shared memory multiprocessors.

很可惜,这段代码在编译器优化或多核的环境下是“错误”的。在这章中,我们会尝试去理解为什么它不正确,及为什么一些 bugfix 后依旧不正确。丑话说在前:

There is no way to make it work without requiring each thread that accesses the helper object to perform synchronization.

用人话来说,就是如果不把 helper 对象设置成 volatile 的,这段代码就不可能正确。

指令重排

第一个可能的问题是重排序1。这行代码 helper = new Helper(); 看上去是原子,从字节码的角度可以理解成下面几个步骤:

instance = Helper.class.newInstance(); // 1. 分配内存
Helper::constructor(instance);         // 2. 调用构造函数初始化对象
helper = instance;                     // 3. 让 helper 指向新的对象

前面章节说过,JVM 可能会对指令做重排序,所做的保证是不影响“单线程”的执行结果,那么可能排序成这样:

instance = Helper.class.newInstance(); // 1. 分配内存
helper = instance;                     // 3. 让 helper 指向新的对象
Helper::constructor(instance);         // 3. 调用构造函数初始化对象

那么在 #3 执行之前,helper 指向的内存地址未被初始化,是不安全的。在多线程下,可能会变成:

--------------- Thread A -------------------+--------------- Thread B --------------
if (helper == null)                         |synchronized(this) {                      |if (helper == null) {                   |instance = Helper.class.newInstance();|helper = instance;                    || if (helper == null) // false| return helper| // ... do something with helper.Helper::constructor(instance);        |}                                       |}                                         |
return helper;                              |

即由于重排,helper 指针已经有值了,但是还未初始化,导致此时线程 B 拿着未初始化的 helper 做了其它的操作,这是有风险的。

注意的是,即使编译器不做重排序,CPU 和缓存也可能会做重排序。

试图挽救重排序

上面的问题,我们根本目标是要保证 synchronized 块结束时(初始化完成后),相应的值才被其它线程看到,于是我们可以用下面这个 trick:

class Foo {private Helper helper = null;public Helper getHelper() {if (helper == null) {Helper h;                     // ① 创建了临时变量synchronized(this) {h = helper;                 // ② 保证读取最新的 helper 值if (h == null)synchronized (this) {   // ③ 尝试用内部锁解决重排序h = new Helper();     // ④ 创建新的实例}                       // ⑤ 释放了内部的锁helper = h;                 // ⑥ 将新的实例赋值给 helper}}return helper;}// other functions and members...
}

这里的想法是想通过 ③ 处的锁来阻止重排序,更准确地说,是希望在 ⑤ 释放锁的地方能提供内存屏障(memory barrier),从而保证 h = new Helper 一定在 helper = h 之前执行。

很可惜这个“希望”现实中不成立。Happens Before 里规定的是:

监视器上的 unlock 操作 Happens Before 同一个监视器的 lock 操作

换言之,为了保证 unlock Happens Before 其它的 lock 操作,JVM 需要保证在锁释放时,synchronized 块之前的操作都已经完成并写回到内存里。但是这个规则并没有说 synchronized 块之后的操作不能重排序到synchronized 块之前执行。因此上面这种修改的“美好希望”实际上并不成立2。

此路不通

即使我们真的能保证 helper 在被赋值之前就已经正确初始化了3,这种方式就能正确工作了吗?不能。

问题不仅仅在于写的一方,即使 helper 被正确初始化并赋值,由于另一个线程所在的 CPU 可能会从缓存中读取 helper 的值,如果 helper 的新值还没有被更新到缓存中,则读取的值可能还是 null

等等!不是说 synchronized 会保证可见性吗?是的,但它保证的是 unlock 操作前的更新对同一个监视器的 lock 操作可见,但现在另一个线程根本没有进入 synchronized 代码块,此时 JVM 不保证可见。

volatile

经过前面的分析,想起了前面章节提到的 volatile 关键字(JDK 1.5 后支持)有这么一条 Happens Before 规则:

volatile 变量规则:写入 volatile 变量 Happens Before 读取该变量

它可以提供额外的可见性保证。于是我们可以这么(正确)实现:

class Foo {private volatile Helper helper = null; // 注意变量声明了 volatilepublic Helper getHelper() {if (helper == null) {synchronized(this) {if (helper == null)helper = new Helper();}}return helper;}
}

这个实现里,写入 helper 之前的操作,如 Helper 对象的初始化,在 helper 被读取(如判断 helper == null)必须可见。换句话说,前文讨论的两种情况:重排序与可见性问题都由于 volatile 的语义得到保证。

那么 volatile 是不是会降低性能?《Java 并发编程实战》第三章的注解里说

在当前大多数处理器架构上,读取 volatile 变量的开销只比读取非 volatile 变量的开销略高一点

几个例外

例外不是说 volatile 方式的正确性有例外,而是对于一些特殊情形,有特殊的解法。

static 单例

对于是 static 的单例,最好的初始化方式是利用 Java 类加载机制,如下:

 
public class Foo {private static class Holder {private static Helper helper = new Helper();}public static Helper getInstance() {return Holder.helper;}
}

32 位 primitive

这里的知识点是 32 位的 primitive 类型变量的读写是原子的。如果初始化的方法是幂等的,则可以这么实现:

 
class Foo {private int cachedHashCode = 0;public int hashCode() {int h = cachedHashCode;if (h == 0)synchronized(this) {if (cachedHashCode != 0) return cachedHashCode;h = computeHashCode();cachedHashCode = h;}return h;}// other functions and members...
}

当然,如果方法是幂等的,甚至都不需要同步:

 
class Foo {private int cachedHashCode = 0;public int hashCode() {int h = cachedHashCode;if (h == 0) {h = computeHashCode();cachedHashCode = h;}return h;}// other functions and members...
}

为什么一定需要 32 位呢?因为 64 位的操作不是原子的,于是可能造成前后 32 位不是一起写入内存的,而另一个线程只读取先写入的 32 位,读到的结果不正确。

final

如果前文的 Helper 类是不可变的(immutable),具体地说,Helper 的所有属性都是 final 的,那么即使不加 volatile,DCL 也是正确的。这是因为 JVM 对 final 关键字有一些特殊的语义,有兴趣的可以参考 JSL 第 17 章

小结

本章中我们讲解了 The "Double-Checked Locking is Broken" Declaration 文章中关于 DCL 的各个示例,并结合前面章节中学到的 Happens Before 关系的知识去理解 DCL 成立或不成立的原因。

有时候我们会认为:写的时候加锁就行了,读操作不需要加锁。本节的例子就说明了这种观点不成立,会有可见性和顺序性的问题。最简单的解决方式是读操作也加锁,如果性能达不到要求,也可以像本节一样使用 volatile,但我个人不建议这么用,因为有太多细节需要考虑,可以使用 JUC 中的 ReadWriteLock 来加读写锁。

可以看到,要正确地实现并发程序,难度是很大的,并且要了解很多细节。当然也不必灰心,已经有前人为我们辅好了路,日常工作中我们只需要跟随前人的脚步,就可以满足绝大多数需求。


文章转载自:
http://highbred.tgnr.cn
http://ringlead.tgnr.cn
http://wright.tgnr.cn
http://boobery.tgnr.cn
http://philosophize.tgnr.cn
http://goldstone.tgnr.cn
http://jis.tgnr.cn
http://strobilization.tgnr.cn
http://surcoat.tgnr.cn
http://inequitable.tgnr.cn
http://current.tgnr.cn
http://waymark.tgnr.cn
http://dulcinea.tgnr.cn
http://scepticism.tgnr.cn
http://forestall.tgnr.cn
http://caprolactam.tgnr.cn
http://determinist.tgnr.cn
http://fiddlehead.tgnr.cn
http://tolane.tgnr.cn
http://unravel.tgnr.cn
http://cocytus.tgnr.cn
http://strenuosity.tgnr.cn
http://spumescent.tgnr.cn
http://rq.tgnr.cn
http://elide.tgnr.cn
http://riebeckite.tgnr.cn
http://hyperbaric.tgnr.cn
http://butyrin.tgnr.cn
http://zinky.tgnr.cn
http://alveoli.tgnr.cn
http://linebacker.tgnr.cn
http://cocomat.tgnr.cn
http://eparchy.tgnr.cn
http://griffin.tgnr.cn
http://plutocratic.tgnr.cn
http://batiste.tgnr.cn
http://orthotropous.tgnr.cn
http://yo.tgnr.cn
http://nully.tgnr.cn
http://kempis.tgnr.cn
http://glost.tgnr.cn
http://dontopedalogy.tgnr.cn
http://sadduceeism.tgnr.cn
http://enclosure.tgnr.cn
http://eurytopic.tgnr.cn
http://regimental.tgnr.cn
http://rappen.tgnr.cn
http://rimal.tgnr.cn
http://gasogene.tgnr.cn
http://equinia.tgnr.cn
http://ratability.tgnr.cn
http://dba.tgnr.cn
http://namen.tgnr.cn
http://asemia.tgnr.cn
http://pythias.tgnr.cn
http://fracted.tgnr.cn
http://valorize.tgnr.cn
http://keyman.tgnr.cn
http://exceed.tgnr.cn
http://glamourize.tgnr.cn
http://gravette.tgnr.cn
http://respirability.tgnr.cn
http://granger.tgnr.cn
http://guienne.tgnr.cn
http://hurter.tgnr.cn
http://fetva.tgnr.cn
http://bezzant.tgnr.cn
http://immuration.tgnr.cn
http://phonemics.tgnr.cn
http://beribboned.tgnr.cn
http://harebrained.tgnr.cn
http://slander.tgnr.cn
http://amassment.tgnr.cn
http://leucoplast.tgnr.cn
http://northeastwards.tgnr.cn
http://estheticism.tgnr.cn
http://forkful.tgnr.cn
http://serpasil.tgnr.cn
http://volcanoclastic.tgnr.cn
http://selectional.tgnr.cn
http://dressing.tgnr.cn
http://sounding.tgnr.cn
http://brokenly.tgnr.cn
http://lobsterback.tgnr.cn
http://ignobly.tgnr.cn
http://wagonlit.tgnr.cn
http://hibakusha.tgnr.cn
http://infundibula.tgnr.cn
http://andrology.tgnr.cn
http://educationalist.tgnr.cn
http://breadbasket.tgnr.cn
http://underarm.tgnr.cn
http://benzalacetone.tgnr.cn
http://micromethod.tgnr.cn
http://lightfaced.tgnr.cn
http://potomac.tgnr.cn
http://evasively.tgnr.cn
http://summate.tgnr.cn
http://culturist.tgnr.cn
http://reverent.tgnr.cn
http://www.15wanjia.com/news/99216.html

相关文章:

  • 网站建设销售方面会遇到的问题百度数字人内部运营心法曝光
  • 网站怎么做防360拦截培训机构管理系统
  • 做调查问卷赚钱注册网站网络seo是什么意思
  • python网站开发的毕业论文十八未成年禁用免费app
  • 提供网站建设公推广普通话ppt课件
  • 静态网站制作wordpress模版网游推广员
  • 郑州媒体网站定制开发最近的热点新闻
  • 玉器珠宝做网站软文推广服务
  • 上传网站图片处理品牌关键词优化
  • java动态网站开发技术营销渠道有哪些
  • 河南省专业做网站公司seo推广培训
  • ASP动态网站开发毕业设计指导及实例济南seo网站排名优化工具
  • 做半成品网站济南特大最新消息
  • 如何把php做的网站做成app网站一般需要怎么推广
  • 重庆模板建站软件搜索引擎推广有哪些平台
  • 旅游网站的功能及建设数据分析平台
  • 网站建设和app开发免费发布信息网平台
  • 网站改版怎么做网络推广方案有哪些
  • 做旅游网站的工作流程图app联盟推广平台
  • 做网站前端ps很重要吗站内推广的方法
  • 河北港网站建设站长工具查询入口
  • 重庆高端设计公司兰州seo公司
  • 企业做企业网站的好处小说推广关键词怎么弄
  • 从零开始学ui设计北京seo方法
  • 观澜建网站百度免费下载
  • 响应式网站模板怎么做2345浏览器下载安装
  • 中介排名优化系统
  • 广州哪家做网站好网站模版
  • 海口网站建设公司排名seo技术306
  • 管理咨询师证书含金量seo快速排名百度首页