volatile是java虚拟机提供的轻量级同步机制。
特点是:
1.保证可见性
即不同线程在对同一个变量进行修改时,保证新值对其他线程的可见。
2.不保证原子性
import java.util.concurrent.atomic.AtomicInteger;
public class VolatileTest {
//private volatile static int num = 0;
private static AtomicInteger num = new AtomicInteger();//AtomicInteger原子类
public static void add(){
//num++;//部署一个原子性操作
num.getAndIncrement();//getAndIncrement()+1方法
}
public static void main(String[] args) {
for (int i = 1; i <= 20; i++) {
new Thread(()->{
for (int j = 0; j < 1000; j++) {
add();
}
}).start();
}
while (Thread.activeCount()>2){
Thread.yield();
}
System.out.println(num);
}
}
3.禁止指令重排
>什么是指令重排
不一定会按照自己写的代码顺序去执行,计算机会重新排序执行顺序。
源代码 -> 编译器优化重排 -> 指令并行也可能会重排 -> 内存系统也会重排 -> 执行
**volatile可以避免指令重排**
内存屏障 cpu指令
1.保证特定的操作执行顺序
2.可以保存某些变量的内存可见性
关于JMM的一些同步约定
1.线程解锁前,必须把共享变量立刻刷刷会主存
2.线程加锁前,必须读取主存中的最新值到工作内存中
3.加锁和解锁必须是同一把锁
**内存交互操作有8种,虚拟机实现必须保证每一个操作都是原子的,不可在分的(对于double和long类型的变量来说,load,store,read和write操作在某些平台上允许例外)**
- lock(锁定):作用于主内存的变量,它把一个变量标识为一条线程独占的状态;
- unlock(解锁):作用于主内存的变量,它把一个处于锁定状态的变量释放出来,释放后的变量才可以被其他线程锁定;
- read(读取):作用于主内存的变量,它把一个变量的值从主内存传输到线程的工作内存中,以便随后的load动作使用;
- load(载入):作用于工作内存的变量,它把read操作从主内存中得到的变量值放入工作内存的变量副本中;
- use(使用):作用于工作内存的变量,它把工作内存中一个变量的值传递给执行引擎,每当虚拟机遇到一个需要使用变量的值的字节码指令时将会执行这个操作;
- assign(赋值):作用于工作内存的变量,它把一个从执行引擎接收的值赋给工作内存的变量,每当虚拟机遇到一个给变量赋值的字节码指令时执行这个操作;
- store(存储):作用于工作内存的变量,它把工作内存中一个变量的值传送到主内存中,以便随后的write操作使用;
- write(写入):作用于主内存的变量,它把store操作从工作内存中得到的变量的值放入主内存的变量中;
如果要把一个变量从主内存拷贝到工作内存,那就要按顺序执行read和load操作,如果要把变量从工作内存同步回主内存,就要按顺序执行store和write操作。注意Java内存模型只要求上述两个操作必须按顺序执行,但不要求是连续执行。也就是说read与load之间、store与write之间是可插入其他指令的,如对主内存中的变量a、b进行访问时,一种可能出现的顺序是read a、read b、load b、load a。除此之外,Java内存模型还规定了在执行上述8种基本操作时必须满足如下规则:
- 不允许read和load、store和write操作之一单独出现,即不允许一个变量从主内存读取了但工作内存不接受,或者工作内存发起回写了但主内存不接受的情况出现;
- 不允许一个线程丢弃它最近的assign操作,即变量在工作内存中改变了之后必须把该变化同步回主内存;
- 不允许一个线程无原因地(没有发生过任何assign操作)把数据从线程的工作内存同步回主内存中;
- 一个新的变量只能在主内存中“诞生”,不允许在工作内存中直接使用一个未被初始化(load或 assign)的变量,换句话说就是对一个变量实施use、store操作之前,必须先执行assign和load操作;
- 一个变量在同一个时刻只允许一条线程对其进行lock操作,但lock操作可以被同一条线程重复执 行多次,多次执行lock后,只有执行相同次数的unlock操作,变量才会被解锁;
- 如果对一个变量执行lock操作,那将会清空工作内存中此变量的值,在执行引擎使用这个变量前,需要重新执行load或assign操作以初始化变量的值;
- 如果一个变量事先没有被lock操作锁定,那就不允许对它执行unlock操作,也不允许去unlock一个被其他线程锁定的变量;
- 对一个变量执行unlock操作之前,必须先把此变量同步回主内存中(执行store、write操作);