Java并发编程

来源:互联网 发布:js离开页面事件 编辑:程序博客网 时间:2024/04/29 22:31

缓存一致性协议

当某个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=743663561value=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) {                    //这一步会被分解为三步操作:                    //1.分配内存                    //2.跟instance赋值,指向新创建的对象                    //3.调用Singleton的构造函数                    //如果不对instance用volatile进行约束,                    //2和3步可能会被重排,造成某一线程拿到的实例还没有被初始化                    instance = new Singleton();                }            }        }        return instance;    }}

不允许对 一个volatile变量的赋值操作与其之前的任何读写操作 重新排序,
也不允许将 读取一个volatile变量的操作与其之后的任何读写操作 重新排序。

0 0
原创粉丝点击