面试的时候经常问到 synchronized与volatile的区别的区别,你可能会奇怪他俩有什么关系吗,如果我觉着关系不大,但是又不能直接说,只能回答他们的共性和区别就ok了
多线程只有保证如下基本特性时才能保证安全:
可见性:线程都是基于cpu的运算,cpu有缓冲区,所以每次在计算的时候都会把数据加载到缓冲区中计算,那计算好的数据最终被会写到主存中,但是有时间差,没有synchronized或者volatile修饰,那么下一个线程读取的时候,读到的数据就有可能是修改前的数据。加上synchronized或者volatile,在该线程释放的时候会把数据写回主存。
主存简单理解为,内存的堆区,声明的属性或new的对象大部分都在堆中。
原子性:就好比你在思考一件事情,不愿意被其他事情打扰一样,线程的原子性也是如此,在执行一段逻辑的时候,也不希望被其他线程干预。多个线程同时请求同一资源或者代码块,这时候对资源或者代码加上synchronized,表示此时只有一个线程能够拿到锁,其他线程只能等待它被释放才能进行下一轮争夺。
有序性或者叫做禁止指令重排序:
处理器为了提高程序运行效率,可能会对输入代码进行优化,所以有可能会调整语句的执行顺序,但是它会保证最终结果的正确性。这种正确性只保证单线程里是没问题的。
int x =1 @1
int y=1;@2
x+y;@3
在处理的过程中可能会按照:@1-->@2-->@3
或者按照 @2-->@1-->@3的执行顺序,但是最终结果都是x+y=2;
int x =1; @1
volatile int y=1;@2
x+y;@3
那他的执行顺序只能是:@1-->@2-->@3
多线程重排序带来的问题:如下代码,如果线程A中发生了重排序,先执行的是inited = true;那么线程B发现inited已经被初始化为true了,那么user就不可能被初始化 ;
线程A中
textA(){
user = loadUser();
inited = true;
}
线程B中
textB(){
if (inited) {
print(user.getId());
}
}
synchronized主要特性:
修饰范围:可以是变量,可以方法。静态和非静态在锁的范围上会有区别。暂时不在这里讲了,后续分几篇专门讲锁。
上述三个特性都有即 可见性;原子性;有序性;
volatile主要特性:
修饰范围:只能是变量;
特性:
可见性;有序性或者叫做禁止指令重排序:不具有原子性;比如i++ 看上去是原子操作,其实并不是,而是分了两部分执行。
如果觉着写的不好请多指教,如果觉着对你有帮助请多关注哈