在实际项目使用并不多,但是容易出错。很多人感觉和synchronized差不多,实际上完全不一样。我们看一下两者的区别:
volatile保证程序的可见性,这个和synchronize的区别就非常大,首先volatile他并不保证原子性。synchronized可以保证原子性,
什么叫原子性呢? 就是多个线程同时操作一个数据或则方法时,他们没有办法同时操作,只有一个操作完毕,另一个才能操作。
什么是可见性呢?线程在访问这样一个变量的时候,他都会去读取最新的值,即便被其他线程修改了。
volatile保证可见性,他的实现原理:内存直读,禁止指令重排序。
内存直读就是每次都从内存中读取,不会走任何的缓存。这样可以保证每次读到的都是新的。
因为变量发生变化的时候,就是内存对应某个地址的值发生变化,不管任何线程去操作,每次都从内存地址里面去读出来的数肯定是在这个时刻最新的值。
那什么情况下会发生读到老的数据呢?
cpu有自己的缓存和寄存器,从内存中拿到数据,然后放到缓存或则寄存器里面处理,这样就有一个时间差。
比如我们有一个数据在没有读到cpu缓存之前就有其他线程来读取,那么他读取到的是什么呢?是缓存中老的值,还没有同步。
非volatile是从缓存里面读,读了再执行,可能被修改
volatile变量直接去内存里面找。保证每次都读最新的。
禁止重排序
首先我们看下什么是cpu指令的重排序,cpu允许将多条指令,不按照程序规定的顺序来执行。我们知道cpu执行会把多条指令进行一定的优化,把他拆成若干块,然后把各部分发给相应的电路单元来处理。A=B B=C c=A A=B B=C 其实cpu只执行两句 A=b b=c 其他的可能会被优化掉
比如,我们new 一个对象,你认为先分配一个块内存地址,再去new 对象,有可能是找到一块缓存区,先构造了一个对象,然后在分配地址,然后在分配指向。和我们想的不一样。
禁止重排序就是,在读取地址内容的时候,在最后增加了一句,内存屏障,他告诉cpu,不允许把这后面内存排到他之前。保证了前面都是有序的。
synchronized
既可以保证可见性,又能够保证原子性。
可见性体现在:通过synchronized或者Lock能保证同一时刻只有一个线程获取锁然后执行同步代码,并且在释放锁之前会将对变量的修改刷新到主存中。
原子性表现在:要么不执行,要么执行到底。
虽然效率比synchronized高,但是容易出粗,不好排查错误,但是不推荐使用。