在这之前我们应该先了解一下Java内存模型
请戳~~~~>Java内存模型
Volatile关键字
保证操作变量的可见性和有序性,不保证操作变量的原子性
这段代码本应该是20000,但是为什么比20000小呢??
因为num++这个指令它并不具有原子性,它是分为几步执行的,volatile能够保证拿到的时候是正确的数据,但是由于它只走了一步,别的线程把这个变量的值改了,它并不知道还是用的之前的数据,所以导致最后的结果比20000小
但是如果用synchronized就会保证原子性
可见性:
A),在汇编层会对volatile修饰的关键字加Lock前缀
Lock前缀指令实际上是一个内存屏障,内存屏障会提供三个功能
- 它确保指令重排序时不会把其后面的指令排到内存屏障之前的位置,也不会把前面的指令排到内存屏障的后面;即在执行到内存屏障这句指令时,在它前面的操作已经全部完成;
- 它会强制将对缓存的修改操作立即写入内存
- 如果是写操作,它会导致其他CPU中对应的缓存行无效
B),在修改变量的过程中
- 将修改变量的副本写入主内存
- 其他线程的 副本置为无效
C),读的时:先判断volatile关键字修饰的变量是否有效,有效直接读取,反之,则 到主内存获取最新值
有序性(能禁止指令重排序)
1)当程序执行到volatile变量的读操作或者写操作时,在其前面的操作的更改肯 定全部已经进行,且结果已经对后面的操作可见;在其后面的操作肯定还没有进行;
2)在进行指令优化时,不能将在对volatile变量访问的语句放在其后面执行,也 不能把volatile变量后面的语句放到其前面执行。
synchronized关键字
保证原子性 :synchronized关键字锁住的代码,一次只能被一个线程所操作。
保证可见性:在Java内存模型中,synchronized规定,线程在加锁时,先清空工作内存,在主存中拷贝最新变量的副本到工作内存,执行完代码后,将更改后的共享变量的值刷新到主内存中,释放锁。
保证有序性:synchronized通过“一个变量 在同一时刻只允许一个线程对它进行lock操作”,这条规则决定了持有同一个锁的两个同步块只能串行的并入
区别
1,volatile不会造成线程的阻塞,synchronized会。
2,synchronized会 造成线程状态的改变,而线程状态的改变又依赖于操作系统,所以效率会比较低。
3,synchronized可以修饰代码块、方法。volatile只能修饰变量。
4,synchronized能保证原子性、volatile不能。
5,volatile本质是在告诉jvm当前变量在寄存器(工作内存)中的值是不确定的,需要从主存中读取; synchronized则是锁定当前变量,只有当前线程可以访问该变量,其他线程被阻塞住。