上 并发编程一:深入理解JMM和并发三大特性

文章目录
深入理解JMM和并发三大特性(上) 前言
JMM属于整个Java并发编程中最难的部分也是最重要的部分(JAVA多线程通信模型——共享 内存模型),涉及的理论知识比较多,我会从三个维度去分析: JAVA层面、JVM层面 、硬件层面 。
在了解JMM和并发编程之前先来看看什么是并发,多线程有什么作用,并发编程出现bug的根本原因是什么 。
并发和并行
并发和并行目标都是最大化CPU的使用率
并行():指在同一时刻,有多条指令在多个处理器上同时执行 。所以无论从微观还是从宏观来看,二者都是一起执行的
并发():指在同一时刻只能有一条指令执行,但多个进程指令被快速的轮换执行,使得在宏观上具有多个进程同时执行的效果,但在微观上并不是同时执行的,只是把时间分成若干段,使多个进程快速交替的执行 。
多线程的作用
对于多线程的作用有这三个:同步、互斥、分工 。
同步:指的是A线程的结果需要依赖B线程的结果 。比如说用户访问开启一个的线程 。而线程会去访问应用程序线程,应用程序线程又会调用jdbc的线程访问数据 。拿到一系列结果后放回给 在放回给用户 。这就是线程之间的同步协作 。
互斥:指的是A线程在使用这个资源,其他线程无法使用,必须等到A线程释放后才能访问该资源 。比如在数据库中对某个数据加了写锁,在一个线程进行写操作的时候另一个线程无法访问到该数据 。
分工:每个线程分配不同的任务,最后结果汇总起来 。比如说在计算很大数据是,开启多个线程,每个线程计算一部分,最后把所有线程计算的结果汇总
并发编程出现bug的根本原因
根本原因在于:可见性、原子性和有序性问题 。这也是并发的三大特性
并发编程三特性
可见性
当一个线程修改了共享变量的值,其他线程能够看到修改的值 。Java 内存模型是通过在变量修改后将新值同步回主内存,在变量读取前从主内存刷新变量值这种依赖主内存作为传递媒介 的方法来实现可见性的 。如何保证可见性:
有序性
即程序执行的顺序按照代码的先后顺序执行 。JVM 存在指令重排,所以存在有序性问题 。如何保证有序性:
原子性
一个或多个操作,要么全部执行且在执行过程中不被任何因素打断,要么全部不执行 。在 Java 中,对基本数据类型的变量的读取和赋值操作是原子性操作(64位处理器) 。不采取任何的原子性保障措施的自增操作并不是原子性的 。如何保证原子性:
下面深入分析这三大特性,在分析三大特性之前还需要了解JMM的内存模型
JMM内存模型
JMM定义:
Java虚拟机规范中定义了Java内存模型(JavaModel,JMM),用于屏蔽掉各种硬件和操作系统的内存访问差异,以实现让Java程序在各种平台下都能达到一致的并发效果,JMM规范了Java虚拟机与计算机内存是如何协同工作的:
规定了一个线程如何和何时可以看到由其他线程修改过后的共享变量的值,以及在必须时如何同步的访问共享变量 。JMM 描述的是一种抽象的概念,一组规则,通过这组规则控制程序中各个变量在共享数据区域和私有数据区域的访问方式,JMM是围绕原子性、有序性、可见性展开的 。
JMM与硬件内存架构的关系
Java内存模型与硬件内存架构之间存在差异 。硬件内存架构没有区分线程栈和堆 。对于硬件,所有的线程栈和堆都分布在主内存中 。部分线程栈和堆可能有时候会出现在CPU缓存中和CPU内部的寄存器中 。如下图所示,Java内存模型和计算机硬件内存架构是一个交叉关系: