volatile这个涉及到JVM内存模型,因为没有读过JVM所以只是理解了很少一点。
先来看个例子:
import java.util.concurrent.TimeUnit;
public class o1volatile {
public static void main(String [] args){
m2 a = new m2();
new Thread(a::m1).start();
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
a.ising = false;
}
}
class m2{
boolean ising = true;
void m1(){
System.out.println("m1开始执行!");
while(ising){
}
System.out.println("条件改变,线程结束!");
}
}
运行结果:
线程迟迟不结束,那么接下来我们改一下代码,把ising声明为volatile
volatile boolean ising = true;
接下来我们来解释一下
JVM内存可见性 (这个我理解的也不是很透彻,只能说一个大概,具体的道友们可以去翻阅深入JVM等资料)
在编程中,线程间通信有两种:
- 共享内存
- 消息传递
共享内存:线程之间共享程序的公共状态,线程之间通过读写内存的主内存(公共内存)来进行隐式的通信。典型的例子比如多线程共享对象进行通信。各个线程共同操作一个对象。
消息传递:需要各个线程通过明确的收发消息来通信,典型的java中就是wait()和notify()。
下面我画一个图大概说一下jvm在多线程下内存分配状态:
先要明确一个概念,多线程所有共享的变量的都是存在主存中的,而每个线程中有一个本地缓存,运行过程中,各个线程会从主存中copy一个变量副本到自己的本地缓存中,对于共享对象的操作全部在本地缓存中取进行,最后在刷新到主存中,这才算一次真正的操作完成。但是各个线程在自己本地缓存中对对象的操作对其他线程来说是不可见的,他们是感知不到的。只有你flush到主存中,然后各个线程从主存中读取变量才能感知到。为了解决这一现象,我们可以使用volatile关键字。该关键字的作用就是保证共享变量,在某线程改变以后,及时flush到主存,并通知其他线程主动来读取我。从而保证各个线程能同事感知到共享变量的变化,从而达到各个线程通信的目的。(不知道我说明白了没有)
这里还有个小尾巴,其实更多时候,当cpu有空闲的时候还是会主动去主存中读取最新的共享变量的信息来同步到本地缓存中的,不信在while循环中睡眠几毫秒,他也是可以停下来的。
volatile 与 synchronized 比较
1.volatile只能声明变量,synchronized 可以控制方法 代码块 。
2.volatile只能保证变量的可见性,并不能保证同步性。而synchronized以锁的形式保证了同步性,也间接保证了可见性。
3.volatile是非阻塞,而synchronized是阻塞式的。
4.volatile主要解决共享变量在多个线程中的可见性,而synchronized解决多个线程访问公共资源的同步性。