摘要:如果编写的并发症程序存在问题,则很难通过调试来解决相应的问题。目前,需要一行检查代码。目前,如果您完全理解和掌握Java的内存模型,那么您很快就会成为Ableanalyalyaly分析并将问题定位。
今天,让我们看一下Java中线程的可见性和顺序如何解决线程。说到这一点,我们必须提及Java的核心技术,即Java的内存模型。
如果编写程序有问题,则很难通过调试来解决相应的问题。目前,需要一系列检查代码。目前,如果您完全理解和掌握Java的内存模型,则可以快速分析,并快速分析和置于问题。
在内存中,Java内存模型规定了所有变量都存储在主内存中(物理内存)。每个线程也都有自己的工作内存,线程的所有操作都必须在工作内存中执行。不同的线程无法访问其他线程的工作内存中的内容。我们可以使用下图来表示交互在三个线程,主内存和工作内存之间。
现在,我们都知道缓存引起了可见性问题,并且汇编和优化导致有序问题。换句话说,解决可见和有序问题的最直接方法是禁用缓存和编译优化。简单地禁用了缓存,编译和优化,我们编写的SO称为高的并发程序的性能不高!它甚至与单个线程程序的性能没有什么不同!有时由于存在竞争性锁,,它可能低于单个线程程序的性能。
因此,由于无法完全禁用缓存和汇编优化,因此如何解决可见性和顺序问题?实际上,合理的解决方案应该是禁用缓存,编译和优化。禁用缓存和按需的汇编优化是什么?简而言之,它是在禁用时禁用的。有些人可能会说,这不是胡说八道吗?实际上,我们继续看不见。
当它被禁用并且无法使用缓存,编译和优化时,可以根据编译高持续性程序的开发人员的要求合理地确定它(此处需要重点)。因此,可以说,为了说,为了说明它是为了说明它解决可见和有序的问题,Java只需要根据需要提供Java程序员即可禁用缓存,编译和优化。
Java内存模型是一个非常复杂的规范。互联网上有很多关于Java记忆模型的文章,但其中大多数是理论,而理论则更多。此处,我不会太多关于Java记忆模型的晦涩的理论知识。开发人员,我们可以以这种方式理解Java的内存模型:Java内存模型指定Java虚拟机(JVM)如何提供禁用缓存和编译优化的方法。
具体来说,这些方法包括:挥发性,同步和最终关键字,以及Java内存模型中的Hapens-Fore-Fore-Fore。
挥发性关键字不是Java所独有的。C语言中也有挥发性的关键字。此关键字的最原始含义是禁用CPU缓存。
例如,我们使用程序中的挥发性关键字来声明一个变量,如下所示。
目前,该变量的此变量的读写不能用于缓存,并且必须从内存中读取和写入。
蓝色虚线箭头表示禁用CPU缓存,而黑色实心箭头则直接从主内存中表示和写入数据。
接下来,让我们一起看一个代码片段,如下所示。
以上示例来自:http://www.cs.umd.edu/~pugh/java/memorymodel/jsr-133-faq.html#finalwrong
在这里,假设线程A执行writer()方法,根据挥发性,v = true将写入v = true;线程B执行读取器()方法,根据挥发性,线程B将从内存中读取变量v。变量v为True,那么此时变量x的值是多少?交叉点?
该示例程序的直觉是x的值为1。实际上,x的特定值与JDK的版本有关。如果JDK版本使用以下1.5以下,则X的值可能为1或0。如果您使用JDK的版本为1.5和1.5,则X的值为1。
当您看到这个问题时,有人会问问题?为什么这是?事实上,答案是在JDK 1.5版本中介绍Java内存模型中的Hapens-Fore-Foreal原理。
我们可以总结如下图所示的Hapens-Forefore原理。
接下来,我们将案例程序结合起来,以解释Java内存模型中的Hapens-Fore-Foreal原理。
按照代码的顺序,在任何操作中,Hapens的操作的先前操作。
例如,[示例1]中的程序x = 1将在v = true之前执行。该规则与单个线程思维更一致:在同一线程中,必须在前面的程序进行修改。关注 - 操作。
对于挥发性变量写作操作,Hapepens-将来会读取此变量读取操作。
换句话说,使用挥发性变量的写作操作首先发生在此变量的阅读操作中。这要求每个人都能理解。
如果在b和b hapens之前发生了c,则是c。
我们将[原理1],[原理2]和[原理3]结合在一起,以查看[示例1]程序。目前,我们可以得出以下结论:
(1)x = 1 hapens-在编写变量之前v = true,它符合[原理1]程序顺序规则。
(2)写变量v = true快乐,读取变量v = true,符合[原理2]挥发性变量规则。
根据[原理3]传输规则,我们可以得出结论,x = 1 hapens-之前读取变量v = true。
换句话说,如果线程b读取v = true,则可见x = 1对螺纹b设置。
实际上,Java 1.5版本的Java.util.concurrent并发工具依赖于挥发性语义来实现可见性。
在锁定操作的操作之前发生,然后是该锁的锁定操作。
例如,输入同步代码块后,以下代码将自动释放锁定。执行代码块后,将自动释放锁定。
我们可以理解此程序:假设变量X的值为10,则线程A执行同步代码块以将X变量的值更改为10并释放同步锁定。线程B进入同步代码块时,您可以获取线程A到X变量的写作操作,也就是说,线程B访问的X变量的值为10。
如果螺纹A调用线程B的启动()方法启动线程B,则start()在线程B中操作。
我们还可以以这种方式理解线程启动规则:线程A启动线程B后,线程B可以在启动线程B之前看到线程A的操作。
让我们看下面的代码。
以上代码是在线程A中执行的代码片段线程B访问。该位置的X变量值为100。
线程A等待线程B完成(线程A中的线程B的JOIN(JOIN()方法),当线程B完成时(线程A呼叫A JOIN(线程b)方法的JOIN()方法),然后线程A可以访问线程B配对变量操作。
例如,在Walter A中执行以下操作。
在中断线程的代码上调用中断线程中断的中断事件。
例如,以下程序代码。在中断线程b中中断b中,在线程a中中断,共享变量x的值修改为100,当线程b检测中断事件时,x变量访问的值为100。
对象的初始化启动了Happy finalize()方法的开始。
例如,以下程序代码。
运行结果如下所示。
使用最终关键字修改的变量将不会更改。但是,在Java 1.5的先前版本中,还将发生由Final修改的变量。在Java 1.5版本之后,Java内存模型对最终关键字修改的变量的重序有一定的限制。只要我们可以提供正确的构造函数,就不会出现问题。
例如,以下程序代码将此值分配给构造函数中的全局变量global.obj。目前,对象初始化尚未完成。目前,对象初始化尚未完成。在此提示时!交点x值通过global.obj读取可能为0。
以上示例来自:http://www.cs.umd.edu/~pugh/java/memorymodel/jsr-133-faq.html#finalwrong
它主要由内存屏障(内存屏障)禁止。即时编译器根据特定的基础体系结构将这些内存障碍替换为特定的CPU指令。对于编译器,内存屏障将限制其可以执行的测序优化。对于处理器,内存屏障将导致高速缓存刷新操作。,对于挥发性,编译器将在挥发性字段的读写操作之前和之后插入一些内存屏障。
本文分享了华为的真诚云社区,作者:宾赫。