Volatile保证多线程下共享变量的可见性和禁止指令重排

Volatile保证多线程下共享变量的可见性和禁止指令重排

一、可见性
多线程下,对于一个没有被volatile修饰的共享变量,当一个子线程对其进行了修改,另一个子线程并不能马上看到修改后的变量,这与Java的内存模式有关。
Java的内存模型规定:所有的共享变量都在主内存中,每个线程都有自己的工作内存,工作内存中的保存了该线程需要用到的主内存中共享变量的副本,线程对变量的所有操作都必须在工作内存中进行,而不能直接对主内存进行操作,并且每个线程不能访问其他线程的工作内存,线程间变量值的传递只能通过主内存来完成。
内存模型图如下图所示:
Volatile保证多线程下共享变量的可见性和禁止指令重排_第1张图片
在多线程环境下,Java内存模型给我们带来的问题:
1.线程1修改的是自己工作内存中共享变量X的副本,而不是主内存中的共享变量X
2.主内存中的共享变量X已经被修改,但线程2读到的还是自己工作内存中的变量
Java为解决这个问题提出了happens-before规则:
happens-before规则:A操作happens-beforeB操作,也就是说A操作的影响能被B所察觉到。
volatile满足了这一规则,一个线程对volatile变量做了修改,其他线程都能马上读到这个修改后的值。
所以一个变量被volatile修饰后,它的读写会变的特别:
1.写一个volatile值的时候,首先修改子线程工作内存中的值,随后立即刷新到主内存中
2.读一个volatile值的时候,volatile会将工作内存中的变量置为无效,直接去主内存中读取
这样对volatile变量值的读写,都是在主内存中进行的,保证了共享变量的可见性。
二、禁止指令重排
指令重排:代码执行时不一定会按照我们定义的执行顺序去执行,有时会发生指令重排,但并不影响单线程下最后的执行结果,所以是被允许的。
很多开发者在写单例模式时,为了满足多线程下的线程安全,会实现为如下形式:
Volatile保证多线程下共享变量的可见性和禁止指令重排_第2张图片
多线程环境下,此种写法提高了程序性能,不要多个线程阻塞等待,但此种情况下会返回一个未初始化的对象,其原因就是指令重排。
创建一个对象需要分为三步:
1.为对象分配内存空间
2.初始化对象
3.将对象指向分配号的内存地址
2、3处可能发生指令重排,当第一个线程创建对象的时候,若发生指令重排,会先执行3,再执行2,3执行完的同时,另外一个线程在判断时返回false,这种情况下就返回了一个未被初始化的对象。
使用volatile后,volatile禁止指令重排,它会在每一条指令前后加上标识,用于顺序执行,可避免指令重排。

你可能感兴趣的:(多线程,java)