Java中的volatile
- Java语言提供了一种稍弱的同步机制,即volatile变量,用来确保将变量的更新操作通知到其他线程。当把变量声明为volatile类型后,编译器与运行时都会注意到这个变量是共享的,因此不会将该变量上的操作与其他内存操作一起重排序。volatile变量不会被缓存在寄存器或者对其他处理器不可见的地方,因此在读取volatile类型的变量时总会返回最新写入的值。
Java 内存模型
- JMM(Java Memory Model):Java 内存模型,是 Java 虚拟机规范中所定义的一种内存模型,Java 内存模型是标准化的,屏蔽掉了底层不同计算机的区别。也就是说,JMM 是 JVM 中定义的一种并发编程的底层模型机制。
- JMM 的规定:
1.所有的共享变量都存储于主内存。这里所说的变量指的是实例变量类变量,不包含局部变量,因为局部变量是线程私有的,因此不存在竞争问题。
2.每一个线程还存在自己的工作内存,线程的工作内存,保留了被线程使用的变量的工作副本。
3.线程对变量的所有的操作(读,取)都必须在工作内存中完成,而不能直接读写主内存中的变量。
4.不同线程之间也不能直接访问对方工作内存中的变量,线程间变量的值的传递需要通过主内存中转来完成。
volatile保证可见性
- 可见性:多个线程共同访问共享变量时,某个线程修改了此变量,其他线程能立即看到修改后的值。
public class demo5 {
volatile static int number = 0;
public static void main( String[] args ) throws InterruptedException
{
new Thread(() -> {
while(number == 0){
}
}).start();
TimeUnit.SECONDS.sleep(2);
number = 1;
System.out.println(number);
}
}
volatile不保证原子性
- 原子性: 一个操作或者多个操作,要么全部执行成功,要么全部执行失败。满足原子性的操作,中途不可被中断。
public class demo6 {
private static volatile int number=0;
public static void add() {
number++;
}
public static void main( String[] args )
{
for(int i=0;i<30;i++){
new Thread(() -> {
for(int j=0;j<1000;j++){
add();
}
}).start();
}
while(Thread.activeCount()>2){
Thread.yield();
}
System.out.println(Thread.currentThread().getName() + " "+ number);
}
}
- 上面例子除了使用lock和synchronized保证原子性之外,还可以使用原子类
public class demo6 {
private static AtomicInteger number = new AtomicInteger();
public static void add() {
number.getAndIncrement();
}
public static void main( String[] args )
{
for(int i=0;i<30;i++){
new Thread(() -> {
for(int j=0;j<1000;j++){
add();
}
}).start();
}
while(Thread.activeCount()>2){
Thread.yield();
}
System.out.println(Thread.currentThread().getName() + " "+ number);
}
}
volatile防止指令重排序
- 有序性:程序执行的顺序按照代码的先后顺序执行。
- 指令重排序:由于JMM模型中允许编译器和处理器为了效率,进行指令重排序的优化。指令重排序在单线程内表现为串行语义,在多线程中会表现为无序。
- volatile是通过编译器在生成字节码时,在指令序列中添加“内存屏障”来禁止指令重排序的。