计算机在执行程序的时候,都是CPU在处理指令,在执行指令的时候,会跟数据打交道,数据存放在主存(物理内存)上,CPU的执行速度要比从内存中读写数据的速度快的多,所以每次操作内存都会消耗好多时间,最终会降低CPU的执行效率;
为了解决这个问题,就在CPU和内存之间增加了一个高速缓存;缓存读写数据的操作要比主存快得多;
使用高效缓存之后,程序运行时,会先将运算需要的数据从主存复制一份到高效缓存中,CPU运行计算时,就可以直接从缓存中获取数据和向缓存中写入数据,运算结束后,再将高效缓存中的数据刷新到主存中;
随着CPU能力的不断提升,一层缓存慢慢就不能满足需求了,就加入二级缓存,甚至是三级缓存;
使用多级缓存之后,CPU要读取数据,就先在一级缓存中找,一级中没有,再去二级缓存中找,若缓存中都找不到,就到主存中去找;
单核CPU有一套L1、L2、L3缓存,若CPU有多个内核,那每个核心都有一套L1、L2缓存,共享L3缓存;
(1)单核CPU、单线程:缓存只有一个线程访问,缓存独占,不会有访问冲突问题;
(2)单核CPU、多线程:进程中的多个线程访问进程中的共享数据,CPU将数据从主存中加载到缓存后,不同线程访问相同的物理地址的时候,都会映射到相同的缓存位置,这样即使发生线程切换,缓存仍然不会失效;由于每个时刻都只有一个线程在执行,所以不会有缓存访问冲突;
(3)多核CPU、多线程:每个核心至少有一个一级缓存,多个线程访问进程中的共享数据,并且多个线程分别在不同的核心上执行,那每个核心都会有一份主存数据的缓存备份,由于多核可以并行,就会出现多个线程同时写各自缓存的情况,最后每个缓存之间的数据就可能不同;
所以,在CPU和主存之间增加高效缓存,在多线程场景下,可能存在缓存一致性问题:多核CPU中,每个核心的自己的缓存中,对同一数据的缓存内容可能不一致;
处理器优化和指令重排
(1)在CPU和主存之间增加高效缓存,在多线程场景下会有缓存一致性的问题;
CPU为了优化程序的执行,可能会进行处理器优化:对代码乱序执行处理;编程语言的编辑器也会做指令重排;
这样就会出现新的问题:
(2)处理器优化问题;
(3)编辑器指令重排问题;
并发编程,为了保证数据的安全,需要满足3个特性:
原子性
:在一个操作中,CPU不可以中途暂停,然后再调度,即不被中断操作,要不不执行,要不就执行完成;可见性
:多个线程访问同一个变量时,一个线程修改了变量的值,其他线程能立刻看到修改的值;有序性
:程序的执行顺序,按照代码的先后顺序执行;
缓存一致性问题就是可见性问题;
处理器优化会导致原子性问题;
指令重排会导致有序性问题;
内存模型
CPU高效缓存、处理器优化、指令重排导致了上面3个问题;要解决这三个问题,就要用到内存模型;
为了保证共享内存的原子性、可见性、有序性,内存模型定义了共享内存系统中多线程程序读写行为的规范;通过这些规范来约束对内存的读写操作,从而保证指令执行的正确性;
内存模型与处理器有关、与缓存有关、与并发有关、与编译器也有关。他解决了CPU多级缓存、处理器优化、指令重排等导致的内存访问问题,保证了并发场景下的一致性、原子性和有序性
Java内存模型(JMM)
内存模型定义了多线程下并发问题的规范,不同语言有不同的实现,Java内存模型就是Java语言对这个内存模型规范的实现;
Java内存模型规定 所有变量都存储在主存中,每个线程有自己的工作内存,称为本地缓存;本地内存保存了该线程中用到的变量数据,它是变量在主存中的拷贝,线程对变量的操作都要在自己的本地内存中进行,不能直接读写主存;不同线程之间不能互相访问对方本地内存中的变量,线程间变量的传递都需要自己的工作内存与主存之间进行数据同步;
Java内存模型就作用在主存与线程工作内存之间;它规定了数据什么时候同步,以及怎么同步;
JMM是一种规范,目的是解决由于多线程通过共享内存进行通信时,存在的本地内存数据不一致、编译器会对代码指令重排序、处理器会对代码乱序执行等带来的问题;
Java内存模型的实现
Java提供了一些和并发处理相关的关键字:volatile、synchronized、final、concurrent;使用这些关键字可以解决多线程并发访问共享数据带来的原子性、一致性、有序性问题;
(1)原子性问题:使用synchronized来保证方法和代码块内的操作是原子性的;
(2)可见性问题:volatile;
Java内存模型是通过 在变量修改后将新值同步回主内存,在变量读取前从主存刷新变量值的方式 解决可见性问题的;
volatile关键字修饰的变量,在修改后可以立即同步到主存,每次使用前都从主存中刷新;
synchronized、final也可以实现可见性,只是实现方式不同;
(3)有序性问题:volatile、synchronized;
volatile会禁止指令重排,synchronized保证统一时刻只允许有一个线程操作;
整理自:https://blog.youkuaiyun.com/hollis_chuang/article/details/80880118