如何学: 分理解并发的三大特性,JMM工作内存和主内存关系,知道多线程之间如何通信的,掌握volatile能保证可见性和有序性,CAS就可以了
目标都是最大化CPU的使用率
并行(parallel):指在同一时刻,有多条指令在多个处理器上同时执行。所以无论从微观还是 从宏观来看,二者都是一起执行的。
并发(concurrency):指在同一时刻只能有一条指令执行,但多个进程指令被快速的轮换执 行,使得在宏观上具有多个进程同时执行的效果,但在微观上并不是同时执行的,只是把时间 分成若干段,使多个进程快速交替的执行。
并发编程Bug的源头:可见性、原子性和有序性问题
当一个线程修改了共享变量的值,其他线程能够看到修改的值。Java 内存模型是通过在变量 修改后将新值同步回主内存,在变量读取前从主内存刷新变量值这种依赖主内存作为传递媒介 的方法来实现可见性的。
如何保证可见性
即程序执行的顺序按照代码的先后顺序执行。JVM 存在指令重排,所以存在有序性问题。
如何保证有序性
一个或多个操作,要么全部执行且在执行过程中不被任何因素打断,要么全部不执行。在 Java 中,对基本数据类型的变量的读取和赋值操作是原子性操作(64位处理器)。**不采取任 何的原子性保障措施的自增操作并不是原子性的。 **
如何保证原子性
内存交互操作
关于主内存与工作内存之间的具体交互协议,即一个变量如何从主内存拷贝到工作内存、 如何从工作内存同步到主内存之间的实现细节,Java内存模型定义了以下八种操作来完成:
volatile修饰的变量的read、load、use操作和assign、store、write必须是连续的,即修改后必须立即同步回主内存,使用时必须从主内存刷新,由此保证volatile变量操作对多线程 的可见性。
通过lock前缀指令,会锁定变量缓存行区域并写回主内存,这个操作称为“缓存锁定”, 缓存一致性机制会阻止同时修改被两个以上处理器缓存的内存区域数据。**一个处理器的缓存回写到内存会导致其他处理器的缓存无效。 **
例如volatile,锁机制
当前线程对当前共享变量的操作会存在读不到,或者不能立刻感知另一个线程对共享变量的写操作
在JVM层面中,在volatile写和volatile读之间,会添加内存屏障storeLoad , 禁止指令重排,从而保证有序性
private volatile static int i;
private static a;
new Thread(() -> {
// volatile 写
i = 2;
// 会添加内存屏障storeLoad , 禁止指令重排,从而保证有序性
// volatile 读
a = i;
},"thread1")
即只要程序的最终结果与它顺序化情况的结果相等,那么指令的执行顺序可以与代码顺序不一致,此过程叫指令的重排序
as-if-serial语义的意思是:不管怎么重排序(编译器和处理器为了提高并行度),(单线
程)程序的执行结果不能被改变。
1. 第二个操作是volatile写,不管第一个操作是什么都不会重排序
2. 第一个操作是volatile读,不管第二个操作是什么都不会重排序
3. 第一个操作是volatile写,第二个操作是volatile读,也不会发生重排序
在JSR规范中定义了4种内存屏障:
由于x86只有store load可能会重排序,所以只有JSR的StoreLoad屏障对应它的mfence或 lock前缀指令,其他屏障对应空操作
CPU缓存即高速缓冲存储器,是位于CPU与主内存间的一种容量较小但速度很高的存储器。由于CPU的速度远高于主内存,CPU直接从内存中存取数据要等待一定时间周期,Cache中保存着CPU刚用过或循环使用的一部分数据,当CPU再次使用该部分数据时可从Cache中直接调用,减少CPU的等待时间,提高了系统的效率。
计算机体系结构中,缓存一致性是共享资源数据的一致性,这些数据最终存储在多个本地缓 存中。当系统中的客户机维护公共内存资源的缓存时,可能会出现数据不一致的问题,这在多处 理系统中的cpu中尤其如此。
总线窥探(Bus snooping)是缓存中的一致性控制器(snoopy cache)监视或窥探总线事务的一种方案,其目标是在分布式共享内存系统中维护缓存一致性
当特定数据被多个缓存共享时,处理器修改了共享数据的值,更改必须传播到所有其他具有 该数据副本的缓存中。这种更改传播可以防止系统违反缓存一致性。
根据管理写操作的本地副本的方式,有两种窥探协议:
Write-invalidate
当处理器写入一个共享缓存块时,其他缓存中的所有共享副本都会通过总线窥探失效。这种 方法确保处理器只能读写一个数据的一个副本。其他缓存中的所有其他副本都无效。这是最常用 的窥探协议。MSI、MESI、MOSI、MOESI和MESIF协议属于该类型。
Write-update
当处理器写入一个共享缓存块时,其他缓存的所有共享副本都会通过总线窥探更新。这个方
法将写数据广播到总线上的所有缓存中。它比write-invalidate协议引起更大的总线流量。这就 是为什么这种方法不常见。Dragon和firefly协议属于此类别。
MESI协议是一个基于写失效的缓存一致性协议,是支持回写(write-back)缓存的最常用
协议。
状态缓存行有4种不同的状态:
缓存行是脏的(dirty),与主存的值不同。如果别的CPU内核要读主存这块数据,该缓存行必 须回写到主存,状态变为共享(S).
缓存行只在当前缓存中,但是干净的–缓存数据同于主存数据。当别的缓存读取它时,状态变为 共享;当前写数据时,变为已修改状态。
缓存行也存在于其它缓存中且是未修改的。缓存行可以在任意时刻抛弃。
缓存行是无效的