前序:JUC,java并行编程中的三个特性
1、原子性:一个或多个操作为一个整体,要么整体执行,要么不执行。synchronized保证代码片段的原子性。
2、可见性:当多个线程共享同一个变量时,若其中一个线程对线程进行了修改,那么该修改对其他线程是可见的。volatile保证变量的可见性
3、有序性:程序执行的顺序和代码的先后顺序是相同的。volatile保证禁止指令的重排优化。
一、Volatile底层原理(主要还是内存屏障)
1、写屏障,通知处理器将写屏障之前的所有已经存储在存储缓存中的数据同步到主内存。
2、读屏障,配合写屏障,使得写屏障之前的内存更新对于读屏障之后的读操作是可见的。
可见性:
写屏障会保证屏障之前的对共享变量的改动,同步到主存中。
读屏障会保证屏障之后的对共享变量的读取,读取的都是主存中的最新数据。
有序性:
写屏障:会确保指令重排时,不会将写屏障之前的代码排在屏障之后。
读屏障:会确保指令重排时,不会将读屏障之后的代码排在屏障之前。
二、JMM(Java 内存模型)
1、java内存模型抽象了线程和主内存的关系,比如线程之间的共享变量必须存储在主内存中。
java内存模型的目的是为了屏蔽系统和硬件的差异,避免一套代码在不同平台下产生效果不同。
jdk1.2之前实现总是从主存中读取变量,是不需要特别的注意的。
而在当前的java内存模型中,线程可以把变量存放在本地内存(机器的寄存器),而不是直接进行在主存中的读写。
这就可能造成一个线程在主存中修改一个变量的值,而另一个线程还在继续使用它在寄存器中的拷贝,造成数据不一致。
解决方案:需要把变量为volatile,这就指示JVM,这个变量是共享且不稳定的,每次使用它都到主存中进行读取。
volatile关键字和synchronized关键字的区别(两个关键字是互补存在的)
1、volatile关键字是线程同步的轻量级实现,所以volatile的性能肯定是比 synchronized关键字要好。
但是volatile关键字只能用于变量,而synchronized关键字可以修饰方法以及代码块。
2、volatile关键字能保证数据的可见性,但不能保证数据的原子性。synchronized关键字能保证数据的可见性和原子性。(可见性:volatile同样可以保证)。
3、volatile关键字主要用于解决变量在多线程之间的可见性,而synchronized关键字解决的多个线程之间访问的同步性。
4、volatile不会造成线程阻塞,synchronized保证线程的阻塞。
5、volatile关键字主要解决变量在多个线程之间的可见性,而synchronized关键字解决的是多个线程之间的资源同步性。
volatile满足可见性、有序性
synchronized保证可见性、原子性、有序性。