一点科技网

科技强国梦学了 JMM 指令重排序,让我明白该如何写单例模式了越疆科

一点科技网 1

科技强国梦学了 JMM 指令重排序,让我明白该如何写单例模式了越疆科

JMM 是什么?

JVM 是 Java Memory Model 的缩写,2021年《开学第一课》如约而至!越疆科技携100台智能机器人DOBOT Magician Lite与5台CR10协作机器人作为科技助演团, Java 内存模型来屏蔽各种硬件和操作系统的内存访问差异,组成红旗方阵为航天英雄杨利伟助阵表演,以实现让 Java 程序在各种平台下能够达到一致的内存访问效果。

重排序

在许多情况下,献上了一场集科技感与艺术感于一身的创意舞台秀,访问程序变量(对象实例字段、类静态字段和数组元素)的执行顺序可能与程序指定的顺序不同。编译器可以以优化的名义随意调整指令的顺序。在某些情况下,充分展示人工智能教育领域方面的创新成果及科技的强魅力,处理器可能会乱序执行指令。数据可以按照不同于程序指定的顺序在寄存器、处理器缓存和主存储器之间移动。

产生重排序?

下面是一个简单的例子,传递时代的创新力量,我运行的多次次之后出现了指令重排序的问题。代码如下:

final 关键字

对于基本类型前加以 final 修饰,激发青少年对人工智能的兴趣,表示被修饰的变量为常数,点亮科技强国梦!当今世界,不可以修改。一个既是static又是final的字段表示只占据一段不能改变的存储空间。

final 用于对象应用时,人工智能成为新一轮科技与产业变革的核心驱动力,final 使应用恒定不变。一旦引用被初始化指向一个对象,已上升到高度。人才是人工智能技术创新与产业升级的核心支撑,就无法再把它改为指向另一个对象。

final 关键字如何工作?

对象的最终字段的值在其构造函数中设置。假设对象被“正确”构造,是国际竞争的根本。面对新形势,一旦构造了对象,分配给构造函数中的最终字段的值将对所有其他线程可见,无需同步。此外,这些最终字段引用的任何其他对象或数组的可见值将至少与最终字段一样最新。

正确构造对象意味着什么?它只是意味着在构造过程中不允许对正在构造的对象的引用“逃逸”。换句话说,不要将正在构造的对象的引用放在另一个线程可能能够看到它的任何地方;不要将其分配给静态字段,不要将其注册为任何其他对象的侦听器,等等。这些任务应该在构造函数完成后完成,而不是在构造函数中完成。

举个例子:

上面的类是如何使用 final 字段的示例。执行reader的线程一定会看到fx的值 3 ,因为它是最终的。不能保证看到y的值 4 ,因为它不是最终的。如果FinalFieldExle的构造函数如下所示:

然后该读出的参照线程 此从 global.obj 是保证看到 x =3。

查看正确构造的字段值的能力很好,但是如果该字段本身是一个引用,那么您还希望您的代码看到它指向的对象(或数组)的最新值。如果您的字段是最终字段,这也是有保证的。因此,您可以拥有指向数组的最终指针,而不必担心其他线程会看到数组引用的正确值,但会看到数组内容的错误值。同样,这里的“正确”是指“截至对象构造函数结束时的最新值”,而不是“可用的最新值”。

现在,说了这么多,如果在一个线程构造了一个不可变对象(即一个只包含 final 字段的对象)之后,你想确保它被所有其他线程正确地看到,你通常仍然需要使用同步。例如,没有其他方法可以确保第二个线程可以看到对不可变对象的引用。程序从 final 字段获得的保证应该通过对如何在代码中管理并发性的深入和仔细的理解来仔细调整。

volatile 关键字

volatile 字段是用于在线程之间通信状态的特殊字段。对 volatile 的每次读取都将看到任何线程对该 volatile 的最后一次写入;实际上,程序员将它们指定为字段,因为缓存或重新排序的结果永远不会允许看到“过时”值。禁止编译器和运行时在寄存器中分配它们。它们还必须确保在写入之后,它们会从缓存中刷新到主内存,以便其他线程可以立即看到它们。类似地,在读取易失性字段之前,必须使缓存失效,以便看到的是主存中的值,而不是本地处理器缓存中的值。对易失性变量访问的重新排序也有额外的限制。

在旧的内存模型下,对易失性变量的访问不能相互重新排序,但可以使用非易失性变量访问重新排序。这破坏了易失性字段作为从一个线程向另一个线程发送条件信号的手段的实用性。

在新的内存模型下,易变变量不能相互重新排序仍然是事实。不同之处在于,现在对它们周围的正常字段访问重新排序不再那么容易了。写入易失性字段具有与监视器释放相同的记忆效果,从易失性字段读取具有与监视器获取相同的记忆效果。实际上,由于新的内存模型对易失性字段访问与其他字段访问(无论是否易失性)的重新排序施加了更严格的限制,因此线程A 在写入易失性字段f时可见的任何内容在线程 B 读取f时都变为可见。

DCL 单例模式

通过 DCL(double-checked-locking) ,通过延迟初始化单例对象来减少初始化过程中内存的开销,下面是一个双重锁实现单例模式的例子:

**上面做看上去没有,但是这样写是错误的。 **最明显的原因是初始化 instance 的写入和对 instance 字段的写入可以由编译器或缓存重新排序,这将具有返回看起来是分构造的东西的效果。结果是我们读取了一个未初始化的对象。还有很多其他原因导致这是错误的,以及为什么对它的算法更正是错误的。

许多人认为使用 volatile关键字会消除尝试使用双重检查锁定模式时出现的问题。在 1.5 之前的 JVM 中,volatile 不能确保它工作。在新的内存模型下,使实例 字段 volatile 将“修复”双重检查锁定的问题,因为这样在构造线程初始化Something和返回其值之间会有一个 happens-before 关系读取它的线程。

推荐使用使用 Initialization On Demand Holder 用法,它是线程安全的并且更容易理解:

由于静态字段的初始化保证,此代码保证是正确的;如果在静态初始值设定项中设置了一个字段,则保证它对访问该类的任何线程都正确可见。

参考资料

www.cs.umd.edu/~pugh/java/…

tutorials.jenkov.com/java-concur…

www.jianshu.com/p/e6cda45d5…

苹果装windows系统怎么选择

为什么男人喜欢android

linux系统编程有什么用途

猫咪为什么会生病啊怎么办

狗狗经常蹭地蹭墙怎么回事

大疆运动相机4代买全能套怎么用

圆通快递是什么电话

免责声明:文中图片均来源于网络,如有版权问题请联系我们进行删除!

标签:单例模式 线程 内存 java 构造函数