Java内存模型(Java Memory Model,JMM)JMM主要是为了规定了线程和内存之间的一些关系。
根据JMM的设计,系统存在一个主内存(Main Memory),Java中所有变量都储存在主存中,对于所有线程都是共享的。
每条线程都有自己的工作内存(Working Memory),工作内存中保存的是主存中某些变量的拷贝,线程对所有变量的操作都是在工作内存中进行,线程之间无法相互直接访问,变量传递均需要通过主存完成。
**Java内存模型(JMM)**是一种抽象的概念,并不真实存在,定义了Java程序在各种平台下对内存访问的机制及规范。线程是程序运行的载体。

内存模型主要是影响线程共享的内存可见性问题,Java线程之间的通信由Java内存模型【JMM】控制,JMM决定一个线程对共享变量的写入何时对另一个线程可见。
从抽象的角度来看,JMM定义了线程和主内存之间的抽象关系:
主内存对应的是Java堆中的对象实例部分,而工作内存对应的则是栈中的部分区域
主内存:
主要存储的是Java实例对象,所有线程创建的实例对象都存放在主内存中。
由于是共享数据区域,多条线程对同一个变量进行访问可能会发生线程安全问题。
工作内存:
主要存储当前方法的所有本地变量信息【工作内存中存储着主内存中的变量副本拷贝】。
每个线程只能访问自己的工作内存,即线程中的本地变量对其它线程是不可见的,就算是两个线程执行的是同一段代码,它们也会各自在自己的工作内存中创建属于当前线程的本地变量,当然也包括了字节码行号指示器、相关Native方法的信息。
并发编程会带来原子性问题、可见性问题、有序性问题
线程之间的通信机制可以分为两种,分别是
Java的并发通信采用的是共享内存的方式。

除了JVM自身提供的对基本数据类型读写操作的原子性外,可以通过synchronized和Lock实现原子性。【synchronized和Lock能够保证任一时刻只有一个线程访问该代码块】
volatile关键字可以保证可见性。
当一个共享变量被volatile修饰时,它会保证修改的值立即被其他的线程看到,即修改的值立即更新到主存中,当其他线程需要读取时,它会去内存中读取新值。
synchronized和Lock也可以保证可见性。
因为它们可以保证任一时刻只有一个线程能访问共享资源,并在其释放锁之前将修改的变量刷新到内存中。
可以通过synchronized和Lock来保证有序性。
synchronized和Lock保证每个时刻是有一个线程执行同步代码,相当于是让线程顺序执行同步代码,自然就保证了有序性。
as-if-serial语义
定义:不管怎么重排序【编译器和处理器为了提高并行度】,(单线程)程序的执行结果不能被改变。
为了遵守as-if-serial语义,编译器和处理器不会对存在数据依赖关系的操作做重排序, 因为这种重排序会改变执行结果。
如果操作之间不存在数据依赖关系,这些操作就可能被编译器和处理器重排序。
happens-before原则
程序顺序原则:在一个线程内必须保证语义串行性,也就是说按照代码顺序执行。
锁规则:解锁(unlock)操作必然发生在后续的同一个锁的加锁(lock)之前,即若对于一个锁解锁后,再加锁,那么加锁的动作必须在解锁动作之后(同一个锁)。
volatile规则:volatile的可见性保证:变量的写,先发生于读。
简单的理解就是,volatile变量在每次被线程访问时,都强迫从主内存中读该变量的值,而当该变量发生变化时,又会强迫将最新的值刷新到主内存,任何时刻不同的线程总是能够看到该变量的最新值。
线程启动规则:线程的start()方法先于它的每一个动作,即如果线程A在执行线程B的start()方法之前修改了共享变量的值,那么当线程B执行start()方法时,线程A对共享变量的修改对线程B可见。
传递性:A先于B ,B先于C 那么A必然先于C。
线程终止规则:场景:主线程A执行时,B线程调用Thread.join()方法。
假设在线程B终止之前修改了共享变量,线程A从线程B的join方法成功返回后,线程B对共享变量的修改将对线程A可见。
线程中断规则:对线程interrupt()方法的调用要比代码检测中断事件先发生,可以通过Thread.interrupted()方法检测线程是否中断。
对象终结规则:对象的构造函数执行,结束先于finalize()【当垃圾回收器将要回收对象时执行】方法。PS:不推荐使用finalize()方法