前言
面试的时候经常被问到synchronized与volatile的区别,你可能会奇怪他俩有什么关系吗,如果觉着关系不大,但是又不能直接说,只能回答他们的共性和区别就ok了。
说到这个两个,那就不得不先说一下线程的安全保证
多线程只有保证如下基本特性时才能保证安全:
可见性:线程都是基于cpu的运算,cpu有缓冲区,所以每次在计算的时候都会把数据加载到缓冲区中计算,那计算好的数据最终被会写到主存中,但是有时间差,没有synchronized或者volatile修饰,那么下一个线程读取的时候,读到的数据就有可能是修改前的数据。加上synchronized或者volatile,在该线程释放的时候会把数据写回主存。
主存简单理解为,内存的堆区,声明的属性或new的对象大部分都在堆中。
原子性:就好比你在思考一件事情,不愿意被其他事情打扰一样,线程的原子性也是如此,在执行一段逻辑的时候,也不希望被其他线程干预。多个线程同时请求同一资源或者代码块,这时候对资源或者代码加上synchronized,表示此时只有一个线程能够拿到锁,其他线程只能等待它被释放才能进行下一轮争夺。
有序性:处理器为了提高程序运行效率,可能会对输入代码进行优化,所以有可能会调整语句的执行顺序,但是它会保证最终结果的正确性。这种正确性只保证单线程里是没问题的
synchronize主要特性
上述三个都有:
可见性
原子性
有序性
volatile主要特性
可见性
有序性(禁止指令重排序)
还没完,看到synchronize和volatile大家都会想到一个设计模式吧,那就是单例模式,那既然synchronize保证的上述三点,为啥单例模式中还要用volatile修饰变量呢?
synchronize:保证原子性、可见性、有序性(多线程顺序执行)
volatile:保证可见性、有序性(禁止指令重排序)
其实这个有序性,一个是保证了多线程执行顺序,一个保证的是指令重拍,很多人都把synchronize的有序性理解错了。
接下来具体跟大家介绍一下单例模式中的两个有序性。
有序性
synchronize只能保证多线程串行执行加锁部分的代码块,但是他不能保证指令执行的顺序,单例模式中锁住的代码块是是一个实例化的过程,那就涉及到指令重拍的问题了。
实例过程:
- 创建内存
- 初始化对象
- 对象指针指向创建的内存
volatile:如果把实例化的3和2顺序颠倒,线程执行完步骤3给对象分配了地址,但是此时线程还没执行2初始化对象,就会导致其他线程拿到实例的对象出错。