volatile

volatile是Java并发编程中的轻量级同步机制,核心解决可见性和有序性的问题

三大特性

复制代码
1.可见性:修改立即可见。  线程A修改了,线程B立即可见。
2.有序性:禁止指令重排。  代码执行的顺序就是执行顺序。
3.不保证原子性:符合操作仍可能出错。  i++这种操作仍需要sychornized或Atomic。

volatile怎么解决可见性

复制代码
volatile修饰共享变量
每个线程读取共享变量时会从主内存中读取,如果他对其进行了操作并写回到主内存中,
其他所有读取了这个共享变量的线程需重新从主内存中读取。
volatile保证了不同线程对共享变量操作的可见性,也就是当以一个线程修改了volatile修饰的变量并写回时,
其他线程会立即看到最新的值

volatile怎么解决有序性

复制代码
为了提高性能,编译器和处理器通常会对编译完成的代码进行指令重排序。
在单线程的情况下是没有问题的,但如果是多线程的话就会引起线程安全问题。
volatile使用了内部屏障来保证有序性
Java编译器会在生成指令系列时在适当的位置会插入内存屏障指令来禁止特定类型的处理器重排序。
写操作:前面代码不能重排到后面;读操作:后面代码不能重排到前面

volatile对比sychornized

复制代码
相比于 synchronized 或者 Lock,volatile 是更轻量的,因为使用 volatile 不会发生上下文切换等开销很大的情况,不会让线程阻塞。但正是由于它的开销相对比较小,所以它的效果,也就是能力,相对也小一些。
相似性:volatile 可以看作是一个轻量版的 synchronized,比如一个共享变量如果自始至终只被各个线程赋值和读取,而没有其他操作的话,那么就可以用 volatile 来代替 synchronized 或者代替原子变量,足以保证线程安全
不可代替:但是在更多的情况下,volatile 是不能代替 synchronized 的,volatile 并没有提供原子性和互斥性。
性能方面:volatile 属性的读写操作都是无锁的,正是因为无锁,所以不需要花费时间在获取锁和释放锁上,所以说它是高性能的,比 synchronized 性能更好

使用场景

复制代码
场景1:状态标志位(最常用)

场景2:单例模式

为什么需要 volatile?

new Singleton() 实际分三步:1.分配内存 2.初始化对象 3.赋值引用。

如果没有 volatile,可能重排为 1→3→2,导致其他线程拿到未初始化的对象(空指针异常)。

复制代码
场景3:读写锁的读操作(简单计数器)