我们都知道在多个线程并发的情况下,会出现脏数据等,这个时候我们需要加锁,一般想到的是synchronized,但是这个时候重量级的锁,需要线程上下文切换和调度,消耗的成本比较高,这时候引入了轻量级的valatile。
官方解释:Java编程语言允许线程访问共享变量,为了确保共享变量能被准确和一致地更新,线程应该确保通过排他锁单独获得这个变量。
通俗来说,就是,多个线程访问对同一个资源进行修改的时候,一个线程改了资源,其他线程也会得到这个消息,保证了数值的一致性;
JMM也是java内存模型,定义程序中各个变量的访问规则,即在虚拟机中将变量存储到内存和从内存中取出变量这样底层细节。此处的变量与Java编程时所说的变量不一样,指包括了实例字段、静态字段和构成数组对象的元素,但是不包括局部变量与方法参数,后者是线程私有的,不会被共享。
主内存与工作内存的交互是按照下面的顺序来的;
是基于JMM内存模型,有一个主内存,然后线程在处理的时候,有自己的缓冲内存,将从主内存中读取到的数据,放到缓冲区中,然后在进行操作。通过下图,来具体介绍一下。
备注对于JMM模型不是很了解的,推荐一篇链接:
https://www.cnblogs.com/nexiyi/p/java_memory_model_and_thread.html
介绍一个小demo,来更深入的了解一下;
public class T {
//(4)使用volatile,将会强制所有线程都去堆内存中读取running的值,是线程终止;
volatile boolean running = true;
void m() {
System.out.println("m start");
while(running) {
try {
TimeUnit.MILLISECONDS.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("m end!");
}
public static void main(String[] args) {
//(1)running是存在于堆内存的t对象中
T t = new T();
//(2)当线程t1开始运行的时候,会把running值从内存中读到t1线程的工作区,在运行过程中直接使用这个copy,并不会每次都去 读取堆内存,这样,当主线程修改running的值之后,t1线程感知不到,所以不会停止运行
new Thread(t::m, "t1").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
//(3)main线程在运行的,时候,将running改为false,为了使t1线程终止
t.running = false;
}