并发预备知识
并发概念:
当拥有多个线程时,程序在单核处理器上运行时,多个线程交替进入内存中,多核处理器上,每个线程都将分配到一个处理器上,是可以同时运行的
cpu多级缓存:
内存和高速缓存都连接在系统总线上
因为cpu的读取速度太快,所以要通过高速缓存来协调,避免造成资源浪费(结构:cpu->cache->memory)
cache存在的意义:缓存要远远小于主存
1.时间局部性:如果某个数据被访问,那么在不久的将来他很有可能再次被访问
2.空间局部性:如果某个数据被访问,那么与他相邻的数据很有可能很快被访问
MESI协议:保证多个CPU cache之间缓存共享数据的一致
MESI代表四种状态
M:modify被修改 它中存储的是该CPU已经修改过的东西(此时内存中的数据还没有被修改),需要返回给内存,返回的时间就是在其他的缓存访问内存之前 在写入内存后,它的状态将变为独享状态
E:Exclusive独享 它中的数据与内存中的数据一致,当有CPU读取时变为共享状态,当有CPU要修改时变为被修改状态
S:share共享 它与主存中的数据是一致的,可以多个CPU同时访问,但如果有一个CPU对其进行修改,那么这个他将转为M,在其他未修改的CPU中转为I状态
I:invalid无效的 无效的
四种操作:
local read: 读本地缓存
local write:向本地写
remote read:从内存中读
remote write:从内存中写
乱序执行优化:处理器为了提高运算速度而做出违背代码原有顺序的优化
Java内存模型:JMM
它规定了一个线程如何,何时可以看到其他线程修改过后的共享变量的值,怎样同步共享变量
堆(Heap):在运行时自动分配内存,但是读取速度慢
栈(Stack):存取速度快
本地变量都是存放在线程栈上的,对象是放在堆中的
当两个线程同时访问一个对象的,每个线程会获得一个该对象的私有拷贝
内存模型的八种操作:
lock(锁定):作用于主内存变量,把一个变量标识为一条线程的独占状态
unlock(解锁):作用于主内存的变量,把一个处于锁定状态的变量释放出来,释放后的变量才可被其他线程锁定
read(读取):作用于主内存的变量,把一个变量值从主内存传输到线程的工作内存中,以便随后的load动作使用
load(载入):作用于工作内存的变量,它把read操作从主内存中得到的变量值放入工作内存的变量副本中
use(使用):作用于工作内存的变量,把工作内存中的一个变量的值传递给执行引擎
assign(赋值):作用于工作内存的变量,它把一个从执行引擎接收到的值赋值给工作内存的变量
store(存储):作用于工作内存的变量,把工作内存中的一个变量的值送到主内存中,以便随后的write操作
write(写入):作用于主内存的变量,它把store操作从工作内存中一个变量的值传送到主内存的变量中
同步规则:
1.如果要把一个变量从主内存中复制到工作内存,就需要按顺序地执行read和load操作,如果把变量从工作内存中同步回主内存中,就要按顺序执行store和write操作,但java内存模型只要求上述操作必须按顺序执行,并没有说要是连续执行的(这里就可以看出,java多线程的执行时会出现不安全问题 )
2.不允许read和load,store和write操作之一单独出现
3.不允许一个线程丢弃它的最近assign操作,即变量在工作内存中改变之后必须同步到主内存中
4.不允许一个线程无原因的(没有发生任何assign操作)把数据从工作内存同步回主内存中(这里可能是说明再非特殊情况下,执行引擎是不能直接和主内存进行数据交换的)
5.一个新的变量只能诞生在主内存中,不允许工作内存中直接使用一个未被初始化(load或assign)的变量,即就是对一个变量实施use和store操作之前,必须先执行过了assign和load操作(这里的意思就是在新变量被初始化后要进行赋值,赋值操
作需要进入到工作内存中让执行引擎来操作,最后返回)
6.一个变量在同一时刻值允许一条线程对其进行lock操作,但lock操作可以被同一条线程重复执行多次,但需要相应执行次数的unlock操作,变量才会被解锁,lock和unlock必须成对出现
7.如果对一个变量执行lock操作,将会清空工作内存中此变量的值,在执行引擎使用这个变量前需要重新执行load或assign操作初始化变量的值
8.如果一个变量事先没有被lock锁定,那就不能对他实行unlock,也不允许去unlock一个被其他线程锁定的变量
9.对一个变量执行unlock之前,必须把此变量同步到主内存中(执行store和write操作)
并发的优势与风险:
优势:同时处理多个请求,响应更快。复杂的操作可以分成多个进程同时进行,程序设计在某些情况下更简单,CPU能在等待IO时做一些其他的事情
风险:多个线程共享数据时可能会产生期望不符的结果,会出现死锁,饥饿问题,CPU切换频繁,调度时间增加,内存消耗