1、计算机

  首先我们需要讲解下计算机的模型:现代计算机模型是基于-冯诺依曼计算机模型

  我们不用管输入和输出设备,最主要的就是中间计算器和存储器之间的交互,也就是CPU与主内存之间取数、存数。

  大家会看到有一个IO总线在进行数据的流通,所以CPU与此磁盘的交互也会通过IO总线,但是IO总线上有其他的一些数据在流通,比如显示器、鼠标键盘等,并且现在的CPU计算速度普遍在GHz,但是我们的内存输出的却是MB,这大大影响了CPU,因为每次取数都要经过IO中线,难免会影响IO,所以CPU有了三级缓存,L1、L2、L3,缓存存储些指令集什么的。如下图:

CPU读取存储器数据过程
  CPU要取寄存器XX的值,只需要一步:直接读取。
  CPU要取L1 cache的某个值,需要1-3步(或者更多):把cache行锁住,把某个数据拿来,解锁,如果没锁住就慢了。
  CPU要取L2 cache的某个值,先要到L1 cache里取,L1当中不存在,在L2里,L2开始加锁,加锁以后,把L2里的数据复制到L1,再执行读L1的过程,上面的3步,再解锁。
  CPU取L3 cache的也是一样,只不过先由L3复制到L2,从L2复制到L1,从L1到CPU。
  CPU取内存则最复杂:通知内存控制器占用总线带宽,通知内存加锁,发起内存读请求,等待回应,回应数据保存到L3(如果没有就到L2),再从L3/2到L1,再从L1到CPU之后解除总线锁定。
  缓存一致性问题:当有多个处理器的时候,如何保证CPU1与CPU2之间的数据是一致的,为了保证数据性一致,需要在访问缓存的同时,遵循一些协议如MESI,最后以谁的缓存为最后结果。

2、什么是线程

  现代操作系统在运行一个程序时,会为其创建一个进程。例如,启动一个Java程序,操作系统就会创建一个Java进程。现代操作系统调度CPU的最小单元是线程,也叫轻量级进程(Light Weight Process),在一个进程里可以创建多个线程,这些线程都拥有各自的计数器、堆栈和局部变量等属性,并且能够访问共享的内存变量。处理器在这些线程上高速切换,让使用者感觉到这些线程在同时执行。线程的实现可以分为两类:
  1、用户级线程(User-Level Thread)
  2、内核线线程(Kernel-Level Thread)

  用户线程:指不需要内核支持而在用户程序中实现的线程,其不依赖于操作系统核心,应用进程利用线程库提供创建、同步、调度和管理线程的函数来控制用户线程。另外,用户线程是由应用进程利用线程库创建和管理,不依赖于操作系统核心。不需要用户态/核心态切换,速度快。操作系统内核不知道多线程的存在,因此一个线程阻塞将使得整个进程(包括它的所有线程)阻塞。由于这里的处理器时间片分配是以进程为基本单位,所以每个线程执行的时间相对减少。
  内核线程:线程的所有管理操作都是由操作系统内核完成的。内核保存线程的状态和上下文信息,当一个线程执行了引起阻塞的系统调用时,内核可以调度该进程的其他线程执行。在多处理器系统上,内核可以分派属于同一进程的多个线程在多个处理器上运行,提高进程执行的并行度。由于需要内核完成线程的创建、调度和管理,所以和用户级线程相比这些操作要慢得多,但是仍然比进程的创建和管理操作要快。大多数市场上的操作系统,如Windows,Linux等都支持内核级线程。

   我们每创建一个线程,jvm都会帮我们在内核空间创建一个线程,这样就避免了jvm来管理线程使操作系统不知道线程的存在,只知道jvm这一个进程,所以一旦jvm中的线程堵塞,CPU是不会进行上下文切换的,导致整个jvm进程都会堵塞。

三、为什么用到并发?并发会产生什么问题?

  并发编程的本质其实就是利用多线程技术,在现代多核的CPU的背景下,催生了并发编程的趋势,通过并发编程的形式可以将多核CPU的计算能力发挥到极致,性能得到提升。除此之外,面对复杂业务模型,并行程序会比串行程序更适应业务需求,而并发编程更能吻合这种业务拆分 。
  即使是单核处理器也支持多线程执行代码,CPU通过给每个线程分配CPU时间片来实现这个机制。时间片是CPU分配给各个线程的时间,因为时间片非常短,所以CPU通过不停地切换线程执行,让我们感觉多个线程是同时执行的,时间片一般是几十毫秒(ms)。
  并发不等于并行:并发指的是多个任务交替进行,而并行则是指真正意义上的“同时进行”。实际上,如果系统内只有一个CPU,而使用多线程时,那么真实系统环境下不能并行,只能通过切换时间片的方式交替进行,而成为并发执行任务。真正的并行也只能出现在拥有多个CPU的系统中。
  并发的优点:
    1充分利用多核CPU的计算能力;
    2方便进行业务拆分,提升应用性能;
   并发产生的问题:
    1.高并发场景下,导致频繁的上下文切换
       2.临界区线程安全问题,容易出现死锁的,产生死锁就会造成系统功能不可用
  CPU通过分配时间片来每次循环任务,每次需要切换到下一个任务时,都会保存上一个任务的结束状态,以便切换回来可以在加载这个任务,所以任务从保存到再加载的过程就是一次上下文切换。

四、什么是JMM模型?

  JMM并不真实存在,只是一种规范,通过这种规范来让定义程序中各个变量的访问方式。JVM运行程序的实体是线程,而每个线程创建时JVM都会为其创建一个工作内存(有些地方称为栈空间),用于存储线程私有的数据,而Java内存模型中规定所有变量都存储在主内存,主内存是共享内存区域,所有线程都可以访问,但线程对变量的操作(读取赋值等)必须在工作内存中进行,首先要将变量从主内存拷贝的自己的工作内存空间,然后对变量进行操作,操作完成后再将变量写回主内存,不能直接操作主内存中的变量,工作内存中存储着主内存中的变量副本拷贝,前面说过,工作内存是每个线程的私有数据区域,因此不同的线程间无法访问对方的工作内存,线程间的通信(传值)必须通过主内存来完成

  JMM是围绕原子性,有序性、可见性展开

JMM-同步八种操作介绍
  (1)lock(锁定):作用于主内存的变量,把一个变量标记为一条线程独占状态
  (2)unlock(解锁):作用于主内存的变量,把一个处于锁定状态的变量释放出来,释放后的变量才可以被其他线程锁定
  (3)read(读取):作用于主内存的变量,把一个变量值从主内存传输到线程的工作内存中,以便随后的load动作使用
  (4)load(载入):作用于工作内存的变量,它把read操作从主内存中得到的变量值放入工作内存的变量副本中
  (5)use(使用):作用于工作内存的变量,把工作内存中的一个变量值传递给执行引擎
  (6)assign(赋值):作用于工作内存的变量,它把一个从执行引擎接收到的值赋给工作内存的变量
  (7)store(存储):作用于工作内存的变量,把工作内存中的一个变量的值传送到主内存中,以便随后的write的操作
  (8)write(写入):作用于工作内存的变量,它把store操作从工作内存中的一个变量的值传送到主内存的变量中
  如果要把一个变量从主内存中复制到工作内存中,就需要按顺序地执行read和load操作,如果把变量从工作内存中同步到主内存中,就需要按顺序地执行store和write操作。但Java内存模型只要求上述操作必须按顺序执行,而没有保证必须是连续执行。

ps:关注一下本人博客,每周都有更新内容哦!

JMM(Java内存模型)是什么?为什么使用并发?的更多相关文章

  1. Java多线程专题2: JMM(Java内存模型)

    合集目录 Java多线程专题2: JMM(Java内存模型) Java中Synchronized关键字的内存语义是什么? If two or more threads share an object, ...

  2. 【Java虚拟机4】Java内存模型(硬件层面的并发优化基础知识--缓存一致性问题)

    前言 今天学习了Java内存模型第一课的视频,讲了硬件层面的知识,还是和大学时一样,醍醐灌顶.老师讲得太好了. Java内存模型,感觉以前学得比较抽象.很繁杂,抽象. 这次试着系统一点跟着2个老师学习 ...

  3. Java 线程 — JMM Java内存模型

    JMM Java Memory Model,Java内存模型,属于语言级的内存模型 并发编程中存在的问题: 如何通信:用于线程之间交换信息.两种方式:共享内存,消息传递 如何同步:用于控制不同线程间操 ...

  4. Java并发编程:JMM(Java内存模型)和volatile

    1. 并发编程的3个概念 并发编程时,要想并发程序正确地执行,必须要保证原子性.可见性和有序性.只要有一个没有被保证,就有可能会导致程序运行不正确. 1.1. 原子性 原子性:即一个或多个操作要么全部 ...

  5. Java并发编程:JMM (Java内存模型) 以及与volatile关键字详解

    目录 计算机系统的一致性 Java内存模型 内存模型的3个重要特征 原子性 可见性 有序性 指令重排序 volatile关键字 保证可见性和防止指令重排 不能保证原子性 计算机系统的一致性 在现代计算 ...

  6. JMM——Java内存模型抽象|八种同步操作|操作规则

    JMM 调用栈&本地变量在线程栈上 对象整体在堆上(包括其本地变量,不论类型),栈有其引用即可访问, 线程调用同一个对象时,是访问该对象的私有拷贝 每个CPU有自己的高速缓存 高速缓存存在意义 ...

  7. JMM - Java内存模型

    内存模型的作用是定义变量的访问规则.包含:实例字段.静态字段.构成数组对象的元素.不包括局部变量和方法参数等线程私有变量. JMM所有变量都在主存,每个线程都有自己的工作内存.线程的工作内存中保存了线 ...

  8. 【Java虚拟机5】Java内存模型(硬件层面的并发优化基础知识--指令乱序问题)

    前言 其实之前大家都了解过volatile,它的第一个作用是保证内存可见,第二个作用是禁止指令重排序.今天系统学习下为什么CPU会指令重排. 存储器的层次结构图 1.CPU乱序执行指令的根源 CPU读 ...

  9. 多线程并发之java内存模型JMM

    多线程概念的引入是人类又一次有效压寨计算机的体现,而且这也是非常有必要的,因为一般运算过程中涉及到数据的读取,例如从磁盘.其他系统.数据库等,CPU的运算速度与数据读取速度有一个严重的不平衡,期间如果 ...

  10. 多线程系列八:线程安全、Java内存模型(JMM)、底层实现原理

    一.线程安全 1.  怎样让多线程下的类安全起来 无状态.加锁.让类不可变.栈封闭.安全的发布对象 2. 死锁 2.1 死锁概念及解决死锁的原则 一定发生在多个线程争夺多个资源里的情况下,发生的原因是 ...

随机推荐

  1. springboot的jar在linux运行

    springboot项目使用maven打包成jar包,如何在linux优雅部署?平时启动项目使用java -jar命令,关闭程序需要查询pid再查杀进程,这样都太麻烦了,今天发现一个博客已经写好的脚本 ...

  2. List的子类特点

    List的子类特点:  ArrayList:        底层数据结构是数组,查询快,增删慢        线程不安全,效率高  Vector:     底层数据结构是数组,查询快,增删慢     ...

  3. 计算机中buffer和cache的理解

    Linux中Buffer和Cache的区别 Cache 和 Buffer的区别 作者:知乎用户链接:https://www.zhihu.com/question/26190832/answer/323 ...

  4. Python __dict__和vars()

    1 __dict__ 设想这样一个场景.有一个字典,从某个地方获取的,比如http请求发过来的,比如从redis中hgetall出来的.我要根据这个字典来构建一个对象. 比如类 class Perso ...

  5. 【java】Java.math.BigDecimal.subtract()方法实例

    java.math.BigDecimal.subtract(BigDecimal subtrahend) 返回一个BigDecimal,其值为 (this - subtrahend), 精度为 max ...

  6. 第二章 c语言概述

    一.#include指令和头文件 1.#include C预处理指令,C编译器在编译前对源代码做一些准备工作 2.stdio.h标准输入输出头文件,提供了关于输入输出的信息供编译器使用 头文件包含了建 ...

  7. HTTP之缓存

    1. 保持副本的新鲜 HTTP 有一些简单的机制可以在不要求服务器记住有哪些缓存拥有其文档副本的情况下,保持已缓存数据与服务器数据之间充分一致.HTTP 将这些简单的机制称为文档过期(document ...

  8. kotlin泛型类型变异

    在java泛型中中会有 ? extends E 可以解决类似于List<String> 赋给List<Object>  的问题,但是在kotlin泛型中并没有提供通配符,而是o ...

  9. Markdown使用TOC自动生成导航栏

    经常使用markdown 的玩家一定很想要一个自动生成的导航栏吧,自己写的基本思路就是 轮询监听滚动条的位置,通过抛锚和跳锚实现,这里介绍一下今天的主角,markdown-toc插件: https:/ ...

  10. ThinkPHP使用smarty模板引擎的方法

    ThinkPHP支持多种php模板引擎,可以根据个人需要加以配置.下面我们以Smarty模板引擎为例,给大家说说具体的操作流程! 首先去Smarty官网上下载一个Smarty.本站下载地址:http: ...