3.volatile详解
参考:
在介绍volatile之前,先讲解下Java内存模型:
Java内存模型
在Java语言中,采取内存模型来实现多线程之间的信息交换和数据同步;
Java内存模型主要功能目标是定义各个变量的访问规则,即在JVM中将变量存储到内存和从内存中取出变量的底层细节
JMM规定所有的变量都存在主内存中,每个线程有自己的工作内存,线程的工作内存保存了改线程使用到的变量的主内存的内存拷贝,线程对所有的操作(save、load)都必须在工作内存中进行,而且不能直接读写主内存中的变量,不同的线程无法访问互相的工作内存,线程之间的传递需要通过主内存来完成;
JMM内存模型围绕并发编程中的原子性、可见性、有序性;
原子性:这个简单就不用说了
可见性:是指一个线程对共享变量做了改变,其他线程能够马上看到这个变量的改变,最常见的是volatile实现了可见性,具体细节可看volatile全解,当然除了volatile之外,例如synchronized、lock、final都能做到,final很好理解,本身就放在常量池里面,那么lock和synchronized如何实现可见性呢?
- synchronized实现可见性:synchronized我们知道通过monitor enter和monitor exit来实现有序性,当同步块开始是,使用共享变量就会取主存中获取最新的值放到工作内存中,在同步快结束时,就会将工作内存中的变量值同步到主存中去,来保证可见性
- lock实现可见性:lock的话通过最常用的ReentrantLock来实现,当我们在方法里面执行lock.lock()的时候,将会重从主存中刷新到工作内存,在finally中的lock.unlock()中会将工作内存中的变量同步到主存中。
有序性:有序性是指对于一个线程代码来说,我们总是以为线程的执行代码是重前往后的,Java通过两个关键字来保证多线程之间操作的有序性,volatile通过内存屏障来保证有序性和禁止指令重排,而synchronized则通过一个变量同一时间只能有一个线程对齐进行加锁并且访问。
happen-before原则:
Java内存模型定义了两项操作之间的次序关系,如果说操作A先行发生于操作数B,者操作数A
Java有很多内置的happen-before如:线程的start 优先于其他;线程的结束晚于所有操作,可以用Thread.join来终结线程;对象的finailize晚于所有的其他操作;
内存之间交互操作
关于主内存与工作内存具体的交互协议,即变量是如何重主存中拷贝到工作内存中的细节,Java提供了8种操作来完成:
lock:作用于主内存的变量,将一个变量标识为一条线程来独占状态
unlock:同上,主要是释放
read:作用于主内存,将主内存同步到工作内存中
load:作用于工作内存变量,将read的值放到工作内存中
use,assign:作用域工作内存
store:作用于工作内存,将要执行主内存的write操作
write:作用于主内存,将store的变量传入主内存中
内存屏障
上面提过,volatile通过内存屏障来保证有序性,那么内存屏障有哪些呢?
参考博文Java内存模型
volatile
理解了Java内存模型,那么volatile就很好理解了,volatile是保证多线程编程中的可见性,volatile通过内存屏障来禁止指令的重排序;具体的细节可以参考java内存模型这章,这里就简单讲下volatile需要考虑的介个问题!
volatile适合用于什么场景?
1.多线程编程里面需要对基本类型做一些线程的可见性
2.禁止重排序的编程模型,如单例模式
volatile是否可以修饰static变量?
volatile的特性是保证变量的可见性已经通过内存屏障来禁止指令的重排序,对于是否是静态的,这个没有明确的规定,也可以使用volatile static String abc="",一般很少这样使用,尽量吧多线程的操作放在一个局部类实例成员方法上,避免其他的工作之外的线程对这个值做修改