1.原子性缓存
原子性是指操做是不可分的。其表如今于对于共享变量的某些操做,应该是不可分的,必须连续完成。例如a++,对于共享变量a的操做,实际上会执行三个步骤,1.读取变量a的值 2.a的值+1 3.将值赋予变量a 。 这三个操做中任何一个操做过程当中,a的值被人篡改,那么都会出现咱们不但愿出现的结果。因此咱们必须保证这是原子性的。多线程
2.可见性:一个线程对共享变量值得修改,可以及时的被其余线程看到 并发
多线程高并发中,线程之间的通讯是靠内存共享,即一个对象或变量,它在堆中的主内存中,每个请求能够看作一个线程,每个线程,都想操做这个主内存的对象或变量,线程会复制一份主内存中的对象或变量,到本身的线程本地栈内存中,直到操做完毕,才会将本地栈内存的对象或变量,赋值给主内存。若是不加同步锁,主内存中的对象或变量,值就容易被最后提交操做的线程覆盖,发生错误。高并发
3.有序性:性能
指令重排序:代码书写的顺序与实际执行的顺序不一样,指令重排序是编译器或处理器为了提升程序性能而作的优化。优化
1.编译器优化的重排序(编译器优化)spa
2.指令级并行重排序(处理器优化)线程
3.内存系统的重排序(处理器优化)对象
答案是否认的。为了讲清楚这个问题,先讲解另外一个概念:数据依赖性blog
若是两个操做访问同一个变量,且这两个操做中有一个为写操做,此时这两个操做之间就存在数据依赖。数据依赖分下列三种类型:
名称 | 代码示例 | 说明 |
写后读 | a = 1;b = a; | 写一个变量以后,再读这个位置。 |
写后写 | a = 1;a = 2; | 写一个变量以后,再写这个变量。 |
读后写 | a = b;b = 1; | 读一个变量以后,再写这个变量。 |
上面三种状况,只要重排序两个操做的执行顺序,程序的执行结果将会被改变。因此,编译器和处理器在重排序时,会遵照数据依赖性,编译器和处理器不会改变存在数据依赖关系的两个操做的执行顺序。也就是说:在单线程环境下,指令执行的最终效果应当与其在顺序执行下的效果一致,不然这种优化便会失去意义。这句话有个专业术语叫作as-if-serial semantics (as-if-serial语义)
int num1=1;//第一行
int num2=2;//第二行
int sum=num1+num;//第三行
单线程:第一行和第二行能够重排序,但第三行不行
重排序不会给单线程带来内存可见性问题
多线程中程序交错执行时,重排序可能会照成内存可见性问题。
synchronized经过操做对象锁和类对象锁,修饰方法和代码块,则保证持有锁的线程操做完毕,从新赋值主内存中的对象或变量后,其余线程再去争抢锁,继续操做主内存对象。达到一个线程的执行结果对其余线程的。知足上面的三个原则。
volatile变量每次被线程访问时,都强迫从主内存中读取该变量的值,而当变量发生变化的时候都会强迫线程将最新的值刷新到主内存中。
这样不一样的变量总能看到最新的值。
能够把volatile变量的单个读写,当作是使用同一个锁对这些单个读/写操做作了同步。
对volatile变量执行写操做时,会在写操做后加入一条store屏障指令
对volatile变量执行读操做时,会在读操做前加入一条load屏障指令
注:因为voaltile比synchronized更加轻量级,因此执行的效率确定是比synchroized更高。在能够保证原子性操做时,能够尽可能的选择使用volatile。在其余不能保证其操做的原子性时,再去考虑使用synchronized。