Java线程安全问题本质是什么导致并发访问冲突?

摘要:原创声明:作者:陈咬金、 博客地址:https:www.cnblogs.comzh94 目录: 线程安全问题的本质 简单理解CPU JVM虚拟机类比于操作系统(可见性 重排序(有序性) 总结 参考链接 线程安全问题的本质 出现线程安
原创声明:作者:陈咬金、 博客地址:https://www.cnblogs.com/zh94/ 目录: 线程安全问题的本质 简单理解CPU JVM虚拟机类比于操作系统(可见性 重排序(有序性) 总结 参考链接 线程安全问题的本质 出现线程安全的问题本质是因为: 主内存和工作内存数据不一致性以及编译器重排序导致。 所以理解上述两个问题的核心,对认知多线程的问题则具有很高的意义; 简单理解CPU CPU除了控制器、运算器等器件还有一个重要的部件就是寄存器。其中寄存器的作用就是进行数据的临时存储。寄存器是cpu直接访问和处理的数据,是一个临时放数据的空间。 CPU读取指令是通过内存去读取的,读一条指令放到CPU 寄存器中,然后CPU去执行处理;所以从内存中去读取指令的速度快慢就决定了这个CPU的执行速度快慢。 无论我们的CPU怎么去升级,如果从内存读取数据的速率问题不解决的话,其CPU的执行性能也不会得到多大的提升。 为了弥补这个问题,在CPU中添加了高速缓存的机制,如ARM A11的处理器,它的1级缓存中的容量是64KB,2级缓存中的容量是8M。 通过增加CPU高速缓存的机制,如果寄存器要取内存中同一内存位置中的数据,则直接从高速缓存中读取即可,无需直接从主内存中进行读取,以此弥补服务器内存读写速度的效率问题,提高CPU的执行速率; 经过简化后的CPU与内存操作的简易图,如下图所示: JVM虚拟机类比于操作系统(可见性) JVM虚拟计算机平台就类似于一个操作系统的角色,所以在具体实现上JVM虚拟机也的确是借鉴了很多操作系统的特点; CPU在计算的时候,并不总是从内存读取数据,它的数据读取顺序优先级 是:寄存器-高速缓存-内存; JAVA中线程的工作空间(working memory)就是CPU的寄存器和高速缓存的抽象描述, Java内存模型中规定了所有的类变量都存储在主内存中,每条线程还有自己的工作内存(类比于CPU的高速缓存),线程的工作内存中保存了该线程使用到的变量到主内存副本拷贝, 线程对变量的所有操作(读取、赋值)都必须在工作内存中进行,而不能直接读写主内存中的变量,操作完成后再将变量写回主内存。不同线程之间无法直接访问对方工作内存中的变量, 线程间变量值的传递均需要在主内存来完成。基本关系如下图: 注意:这里的Java内存模型,主内存、工作内存是一个抽象的概念与Java内存区域模型的Java堆、栈、方法区本质上不是同一层次的内存划分。 尽管Java内存模型和Java内存区域模型的划分不是一个层次的划分。“但是如果将Java内存模型中的工作内存和主内存一定要落实到对应的Java内存区域模型中时”, Java工作内存又可以被称作为栈,而主内存则对应的是Java内存区域模型中的堆;所以后续提到的线程工作内存的概念时,读者也可以直接理解为线程的栈空间即可,而主内存则直接对应到堆空间上即可; Java 内存模型对应英文为(Java Memory Model 简称为 JMM)之所以说JMM和Java内存区域模型并非一个层次的划分的原因是:JMM 本身是一个抽象的概念,并不具体存在,它所描述的是一组规范或者说规则,通过这种规则定义了 Java程序中各个变量的访问方式。 请仔细理解一下这句话“JMM的规则定义了 Java程序中各个变量的访问方式”; 那么在JAVA程序中,各个变量的访问方式是什么样的? 在JAVA中我们常知的变量定义有:类的实例变量,静态变量;那么这几种变量的访问规则是什么样的? 在JMM的规范中定义如下:在Java中所有的变量都是存储在主内存堆中,主内存是共享内存的区域,所有的线程都可以访问。 但线程对变量的操作(读取,赋值)则必须在线程的工作空间(栈)中执行;所以在线程的执行过程中,首先需要将变量从主内存中拷贝到自己的工作空间中,然后对变量进行操作, 操作完成后,再将变量写回主内存中。所以线程的工作空间中是不能直接操作主内存中的变量,而是操作的是主内存中的变量副本的拷贝。
阅读全文