JMM模型是什么
是一种抽象的概念,并不是真实存在的,其实就是一组规则或规范。规定了所有变量都存储在主内存种,主内存是共享内存区域,所有线程都可以访问,但是对变量的操作(读赋值等)必须在工作内存中进行。工作内存是每个线程的私有数据区域,不同线程间无法访问对方的工作内存,线程间的通信(传值)必须通过主内存来完成
如图
主内存
其共享数据区域,存储是java实例对象,所有线程创建的实例对象都存放在主内存中,包括:成员变量还是方法中的本地变量(局部变量),共享的类信息,常量,静态变量。
工作内存
主要存储着主内存中的变量副本拷贝,每个线程只能访问自己的工作内存,其他线程是不可见的,即使是同一段代码,因此存储在工作内存的数据不存在线程安全问题。
JMM存在的必要性
由于JVM运行程序的实体是线程,而每个相册创建时jvm都会为其创建一个工作内存,用于存储线程私有数据,线程与主内存中的变量操作必须通过工作内存进行交互完成,主要过程是将变量从主内存拷贝到每个线程各自的工作内存中,然后对变量进行操作,操作完成后再将变量写回主内存,如果存在两个线程同时对一个主内存中的实例对象的变量进行操作就有可能诱发线程安全问题。
java内存模型只要求上述操作必须按顺序执行,而没有保证必须是连续执行。
并发编程的原子性,有序性,可见性问题
原子性
原子性指的是一个操作是不可中断的,即使在多线程环境下,一个操作一旦开始就不会被其他线程影响的,这个大家都常见到java中的synchronized和lock。
可见性
可见性指的是当一个线程修改了某个共享变量的值,其他线程是否立马知道这个修改的值。
在多线程的情况下可就不一定了,由jmm理论模型知道,线程对共享变量的操作都是线程拷贝到各自的工作内存进行操作后才写回主内存中,这个时候就回存在一个问题,线程A在对共享变量进行操作的时候,还未写回主内存中,另外一个线程B又对主内存中同一个共享变量进行操作,由于A线程中工作内存中的共享变量对线程B来说并不可见,这种就出现问题了,这种工作内存与主内存同步延迟现象就造成了可见性问题。volatile关键字保证可见性。一个共享变量被volatile修饰时,它会保证修改的值立即被其他线程看到。
有序性
在单线程而言,代码的执行按照顺序执行的,这样理解并没有毛病,但是多线程环境下,会出现乱序的情况,因为程序编译成机器指令后可能会出现指令重排现象,重排后与原指令不一样。通过volatile保证的。
java内存模型:由于每个线程都有自己的工作内存,线程对变量的所有操作都必须在工作内存*进行的,而不能直接对主存进行操作,每个线程都无法访问其他线程的工作内存。java内存模型具备先天的“有序性”,通常称为happens-before原则。但是,如果两个操作的执行次序无法从happens-before原则推导出来,那么它们就不能保证它们的有序性,虚拟机可以随便地对他们进行指令重排序。(只要程序的最终结果与它顺序化情况的结果相等,JVM能根据处理器特性(cpu多级缓存系统,多核处理器等)适当对机器指令进行重排序,)
happens-before原则:
1.一个线程来说按照代码顺序执行、
2.锁规则:同一把锁,在加锁之前必须要又解锁动作的发生
3.volatile规则:被volatile修饰的变量每次被线程访问时,都强迫从主内存中读该变量,而当该变量发生变化时,又会强迫将最新的值刷新到主内存中。
4.线程启动规则:start()先于它的每一个动作,如果线程A在执行线程 B的start方法之前修改了共享变量的值,那么线程B执行start方法时,A的共享变量B可见
5.传递性:a先于b,b先于c,则a必先于c
6.线程终止规则:线程所有操作先于线程终止,线程B终止之前,修改了共享变量,线程A从B的join成功返回,线程B对共享变量修改对A可见
7.对象终结规则:构造函数执行,结束先于finalize()方法。