以几个问题的方式,进入学习:
Q:为什么要用多线程?
A:1.随着硬件的快速发展,计算机的运算能力越来越强大
2.计算机的运算速度与它的存储和通信子系统速度差距太大,大量的时间花费在磁盘I/O、网络通信和数据库访问上。
因此必须使用一些手段去把处理器的运算能力“压榨”出来,同时处理多项任务就是最有效的手段了
Q:除了“同时处理多个任务”,还可以如何提高处理器的效率(毕竟过于频繁切换任务也是很消耗资源的)?
A:引入“高速缓存”来作为内存与处理器之间的缓冲:将需要计算的数据先复制到缓存中,让运算快速完成,运算结束后,再从缓存同步结果回内存中。这样处理器就无须等待“缓慢”的内存读写了
一致性问题
- 引入“高速缓存”后,很好的解决了处理器和内存速度之间的矛盾。但是它也引入了新的问题:缓存一致性(Cache Coherence)。在多处理器系统中,每个处理器有自己的高速缓存,而他们又共享同一主内存。当多个处理器的运算涉及到同一块数据,将可能导致各自的缓存数据不一致。
- 为了解决“一致性”问题,需要各个处理器访问缓存时,遵循一些协议。如MSI,MOSI,Dragon Protocol等
- 另外,为了使得处理器内部的预算单元能尽量被充分利用,处理器可能还会对输入的代码进行乱序执行优化(类似JVM中的指令重排序)
Java内存模型
-
目标:Java内存模型主要目的是定义程序中各个变量的访问规则。(注:这里的变量包括:实例字段、静态字段和构成数组对象的元素,不包括线程私有的变量:局部变量和方法参数等)
-
另外我们要清楚:
- Java所有变量都是存储在主内存中。
- 线程会将使用到的变量从主内存拷贝(有深浅拷贝)副本,在工作内存中进行对变量的所有操作。
- 线程间所有的变量值传递均需要通过主内存来完成
-
Java内存间交互操作
- java内存模型中定义了8中操作和8条规则来完成工作内存和主内存之间的交互:
操作 | 含义 |
---|---|
lock(锁定) | 作用于主内存的变量,把一个变量标识为一条线程独占的状态 |
unlock(解锁) | 作用于主内存的变量,把一个处于锁定的变量释放出来,释放后的变量才可以被其他线程锁定 |
read(读取) | 作用于主内存的变量,把一个变量的值从主内存中传输到线程的工作内存中,以便load动作使用 |
load(载入) | 作用于工作内存的变量,把read操作从主内存中得到的变量放入工作内存的副本中 |
use(使用) | 作用于工作内存的变量,把工作内存中的一个变量的值传递给执行引擎,每当JVM遇到一个需要使用变量的指令时,就需要使用到这个 |
assign(赋值) | 作用于工作内存的变量,把一个从执行引擎接收到的值赋给工作内存的变量 |
store(存储) | 作用于工作内存的变量,它把工作内存中的一个变量的值送到主内存,以便write操作使用 |
write(写入) | 作用于工作内存的变量,它把store操作从工作内存中得到的变量的值放入主内存中 |
0 | 基本操作八条规则 |
1 | 不允许read和load、store和write操作之一单独出现 |
2 | 不允许一个线程丢弃它最近的assign操作,即变量在工作内存中中修改了值后,必须把值传递回主内存 |
3 | 不允许一个线程无原因(没有发生过assign)就把数据从工作内存同步回住内存 |
4 | 一个新的变量只能在住内存中“诞生”,不允许在工作内存中直接使用一个未被初始化(load和assign)的变量。也就是,对一个变量use和store之前,必须先assign和load |
5 | 一个变量在同一时刻只允许一条线程对其lock,但是lock操作允许被同一条线程操作多次,但是多次lock后,要多次unlock |
6 | 如果对一个变量执行lock操作,将会清空工作内存中此变量的值,在执行引擎使用这个变量前,需要重新执行load或assign操作,初始化变量的值 |
7 | 如果一个变量没有被lock,就不允许对其进行unlock操作,也不允许对其他线程lock的变量进行unlock |
8 | 对一个变量进行unlock之前,必须先把此变量同步回住内存(store,write操作) |