Java内存模型和线程
前提:多cpu环境下的并发
Overview
- 近年来硬件的发展从追求处理器频率到追求多核心并行处理器。
- 内存模型:在特定操作协议下,对特定内存或者cache进行访问的过程抽象。
- 多cpu环境下cpu速度>>Io速度,因此设立高速缓存来弥合cpu和内存的速度差异->然而每个处理器都有缓存导致了一致性问题->因此需要根据规定来访问缓存以保证一致性。
JMM
- JMM(java内存模型),用以屏蔽不同硬和os的内存访问差异,主要目标是定义程序中各个共享变量的访问,即读写内存的规则
模式
- 线程拥有自己的工作内存。线程对变量的多有操作都必需在工作内存中进行,不能直接读写主内存中的变量。
- 线程间的变量传递通过主内存完成。线程间无法互相访问工作内存.
- 线程的工作内存中保存了被该线程使用到的变量的主内副本拷贝
先行先发生原则,用来确定一个访问在并发环境下是否安全
- volatil可以防止指令重排。将本cpu的cache写入内存,并使得其他cpu的cache无效来使得对其cpu立即可见。
7:java内存模型是围绕着在并发过程中如何处理原子性,可见性和有序性这3个特征来建立的。
(1)可见性:当一个线程修改了共享变量的值,其他线程能够立刻得知这个修改。通过主内存来实现。对一个变量执unlock操作之前,必须先把此变量同步到主内存中。
(2)有序性:线程内表现为串行。指令重拍和工作内存与主内存同步延迟。
8:synchronized可以保证这3种特性,但是开销较大。
9:时间先后顺序和先行发生原则之间基本没有太大的关系。衡量并发安全问题的时候一必须以先行发生原则为准。
10:先行发生原则:java内存模型中定义的两项操作之间的偏序(自反,反对称,传递)关系(保证依赖关系)。
Java与线程:
1:进程是资源分派单位
2:线程是cpu基本的调度单位。
3:java线程模型基于操作系统原生线程模型来实现。(也就是依赖特定操作系统的线程实现)
4:线程模型只对线程的并发规模和操作成本产生影,对java程序的编码和运行美影响。
5:java使用抢占式线程调度,每个线程由系统来分配执行时间。
线程安全及锁优化:
1:面向过程思想:站在计算机的角度。将数据和代码看做独立的部分。数据代表问题空间中的客体,程序代码用于处理数据。
2:面向对象:站在现实世的角度去抽象和解决问题,将数据和行为都看做对象的一部分,这样可以和程序员能以符合现实世界的思维来编写和组织程序。
3:首先要保证并发的正确性,然后在此基础实现高效。
4:同步:保证共享数据同一时刻只被一个线程使用。互斥是实现同步的一种手段。
5:synchronized同步块对于同一条线程来说是可重入的,不会出现自己吧自己锁死的情况。
6:乐观并发锁。先进行操作,如果产生了冲突,再采取补偿措施。
7:自旋锁:避免阻塞线程,唤醒线程导致线程切换的开销。但是占用了处理器时间。短时间的等待可以。长时间还是阻塞了比较好。
8:锁消除,锁粗化。都是编译优化的结果。
9:轻量级锁:在没有多线程竞争的前提下,减少传统的重量级锁使用操作系统互斥量产生的性能消耗。依据为:对于绝大部分锁,在整个同步周期内都是不存在竞争的。
10:偏向锁:消除数据在无竞争情况下的同步原语,进一步提高程的运行性能。(就是先假设没竞争,设置个标志,运行时都不同步来减少开销。一旦发现要竞争,还是得按同步来。)