Java内存模型
1 线程之间如何通信
1.1 共享内存的方式
1.1.1 处理器写缓存区
1.1.2 总线
1.1.3 共享内存
1.2 消息传递的方式
1.2.1 线程之间没有公共状态,必需通过消息的方式通信
2 从源代码到指令序列的重排序
2.1 重排序分类
2.1.1 编译器优化重排序
2.1.2 处理器重排序
2.1.2.1 内存系统重排序
2.1.2.2 指令级并行重排序
2.2 重排序的影响
2.2.1 单线程中,不影响线程运行结果,给你的幻觉是,它不存在
2.2.2 多线程中,无形中影响运行结果,也是让人产生错觉
2.3 禁止特定类型的重排序
2.3.1 内存屏障
2.3.1.1 loadlaod
2.3.1.1.1 load1 loadlaod load2,确保load1数据的装载先与load2及以后的所有数据装载指令的执行
2.3.1.2 loadstore
2.3.1.2.1 load1 loadstone store1 确保laod1数据的加载先与store1以及后面所有存储指令刷新内存
2.3.1.3 storestore
2.3.1.3.1 store1 stroestore store2 确保store1的存储指令(属性内存)先有store2以及后面的所有存储指令的刷新内存
2.3.1.4 storelaod
2.3.1.4.1 store storeload load store 确保store的存储指令先与load以及后面所有的装载操作
2.4 单线程的重排序 as-if-serial
2.4.1 单线程在不改变运行结果的情况下,多有的 编译器,runtime,处理器都会遵循as-if-serial语义进行重排序
3 数据竞争
3.1 一个线程在写一个变量,另外一个线程在读一个变量,而两个线程没有通过同步来排序
4 顺序一致性
4.1 科学家意淫出来的模型
4.2 多个线程按顺序执行、单个线程内部按指令顺序执行
5 JMM中的顺序一致性
5.1 多个线程按照顺序执行,边界内部重排序执行
5.2 long与double型数据写操作不具备原子性
5.2.1 在计算机中,数据通过总线在处理器与内存中交互。每次交互的多个操作,称作一个总线事务,而总线会同步事务,一个时刻,只有一个线程与内存交互,串行化了多个处理器对内存的访问,在某些处理器中,要保证64位数据的写操作会有比较大的开销,这样,妥协之后,java内存模型,鼓励但是不强求jvm对64位long和double具有原子操作,这样一次写,会落在两个事务中,此时就不具有原子性了
6 volatile的内存语义
6.1 volatile特性
6.1.1 内存可见性
6.1.2 操作原子性
6.2 volatile内存语义
6.2.1 写时,JMM会把对应线程的本地缓存刷新到内存中
6.2.1.1 写前,加一个storestore
6.2.1.2 写后,加一个storeload
6.2.2 读时,JMM会把对应线程会本地缓存置为无效,重新装载内存数据
6.2.2.1 读后,加一个loadload
6.2.2.2 读后,加一个loadstore
7 锁的内存语义
7.1 get锁时,JMM会把内存的本地缓存置为无效
7.2 release锁时,JMM会把对应线程的内存刷到内存中
7.3 ReentrantLock的源码实现
7.3.1 重人锁中,组合了一个Sync的对象,这个对象继承了AbstracQueueSynchronizer对象,acquire与release两个方法,AQS对象通过volatile来实现锁的控制
8 Concurrent包的实现
8.1 套路
8.1.1 申明共享变量volatile
8.1.2 用CAS来更新实现线程之间的同步
8.1.3 陪volatile的读写与CAS的内存语义,实现内存间通信
9 final内存语义
9.1 写时的重排序规则:JMM禁止编译器把final域的写重排序到构造函数之外,也就是说,在引用对象被其他线程可见之前,final域已经被初始化
9.1.1 写操作之后,有storestore
9.2 读时的重排序规则:在一个线程中,初次读对象引用与初次读该对象包含的final域,这两个操作,不允许重排序。也就是说,在读这个final域之前,一定得要先读这个对象引用
9.2.1 读操作之前,有loadload
10 对象逸出
10.1 对象在一个线程中,没有完全初始化好了之前,就被其他线程引用,这就是说,这个对象在构造函数中“逸出”。
10.2 对象“逸出”,导致final域的值两次读的不一样,第一次读的时候,因为引用对象还没完成初始化好,final域也还没执行。第二次读的时候,final域已经执行完了
11 happens-before
11.1 JMM对于编译器,处理器没有啥特别的要求,唯一要求的就是,在不改变程序的运行结果的前提下,你爱怎么玩就怎么玩,我不管
11.2 定义
11.2.1 一个操作happens-before另外一个操作,那么第一个操作的执行结果将对第二个操作可见