缓存一致性协议
当某个CPU核心写数据时,如果发现其中有变量被其他CUP核心共享,则会通知那个CUP核心将该变量缓存置为无效,如果用到时再去内存重新读取。如此可以保证多个CPU共享同一变量的一致性。
MESI的状态
M(Modifed) 数据有效,CPU的数据被修改了,且与内存中不一致,数据目前只存在于当前CPU的缓存中。E(Exclusive) 数据有效,数据与内存中的数据一致,不与其他CPU缓存同步。S(Shared) 数据有效,数据与内存中的数据一致,且存在于很多CPU缓存中。I(Invalid) 数据无效。
指令重排
为了提高性能,编译器常常会对指令进行重新排序。
编译器优化的重新排序。编译器在不改变单线程程序语义的前提下,可以重新安排语句的执行顺序。如定义变量的顺序等。指令集并行的重排序。如果不存在数据依赖性,处理器可以改变单条语句对应机器指令的执行顺序。内存系统的重排序。由于处理器使用缓存和读/写缓冲区,这使得加载和存储操作看上去可能是在乱序执行。
并发编程
在并发程序存在共享数据的情况下,必须要保证原子性、可见性及有序性。保证程序的正确运行。 编写线程安全的代码,本质上就是管理对状态(state)的访问,尤其是共享的、可变的状态,即变量。
Java内存模型
这是逻辑上的内存模型。
可见性:
public class ThreadTest extends Thread{
boolean stop =
false;
int value =
0;
public void run() {
while(!stop) {
value ++;
}
}
public static void main(String[] args)
throws InterruptedException {
ThreadTest t =
new ThreadTest();
t.start();
Thread.sleep(
2000);
t.stop =
true;
System.out.println(
"value=" + t.value);
Thread.sleep(
2000);
System.out.println(
"value=" + t.value);
t.stop();
}
}
执行结果:
value=
743663561
value=
1492400047
t线程每次都是在自己的缓存中读取stop,所以while循环一致都没有停下来,如果在stop变量前加上volatile关键字,那么线程每次调用都会到主存中重新读取。也就是说volatile实现了可见性。
有序性与原子性
public class Singleton {
private static volatile Singleton instance;
private Singleton(){}
public static Singleton
getInstancne() {
if (instance==
null) {
synchronized (Singleton.class) {
if (instance==
null) {
instance =
new Singleton();
}
}
}
return instance;
}
}
不允许对 一个volatile变量的赋值操作与其之前的任何读写操作 重新排序, 也不允许将 读取一个volatile变量的操作与其之后的任何读写操作 重新排序。