多线程synchronized 和 volatile 关键字

浏览了一天的博文,大概总结出个人看法,以备以后继续学习

1、必须明白一个知识点,内存屏障;

下面是基于保守策略的JMM内存屏障插入策略:
在每个volatile写操作的前面插入一个StoreStore屏障。
在每个volatile写操作的后面插入一个StoreLoad屏障。
在每个volatile读操作的后面插入一个LoadLoad屏障。
在每个volatile读操作的后面插入一个LoadStore屏障。

以上就是四种内存屏障类型,加入内存屏障可以保证内存屏障前后的指令顺序执行;
个人理解,内存屏障只能保证屏障前后指令执行顺序,但是无法保证单侧指令顺序;内存屏障就好像一道道关卡,必须一关一关的过,但是在某一关你想按照什么顺序过由你自己决定,当然不能as-if-serial语义和happens-before规则。

2、happens-before规则
太多了,记住一点吧:volatile变量的写操作happens-before其他任何操作

3、as-if-serial语义
单线程环境下,指令重排序不能影响单线程执行结果

4、多线程三要素:原子性、有序性、可见性
有序但是不一定可见,原因,多线程按一定顺序执行,但是如果线程A的修改只发生在工作内存,还没有write进主存,那么线程B读取到的依旧是老数据,所以不满足可见性。

5、指令重排:优化指令运行,避免某一变量被CPU重复加载。

现在来记录synchronized 和 volatile关键字

synchronized :
1、用法:只能用在方法修饰符或者代码块上;
2、用在静态方法、或者synchronized(类.class)锁的是类的Class对象,所有类的任何对象访问的时候都会加锁;
3、synchronized(this或Object)是对Object加锁,同一对象访问会生效cpu不会在线程之间切换,不同对象访问不会生效;
4、属于可重入锁;
5、加锁时工作内存失效,区主存读取数据;
6、释放锁之前,先把工作内存数据刷新到主存。
7、为什么synchronized 内对象new的过程中,初始化与赋值会重排序,因为在关键字内部,所有可以重排。

volatile:
1、用法:只能用在变量上;
2、根据happens-before规则,保证内存屏障前后执行顺序;
3、volatile变量修改之后会立即刷新到主存,并使其他线程的工作内存失效,要求其他线程强制到主存读取数据;
4、为什么volatile线程不安全?
i++,线程a、b同时读取1,a加一后,更新到主存变成2,并使b工作内存失效,但是b已经读取到i的值,不会再去主存读取,那么通过b也会更新主存为2,就导致数据不一致。

二者对比:

相同点:
1、二者都会在编译期往字节码中插入内存屏障;
2、都可以保证及时更新主存,个人理解:操作依旧发生在工作内存,大家都说会及时更新到主存,name我觉得这两个关键字由于内存屏障的原因会把写工作内存,写主存这两步变成不可分割的原子操作,主存更新之前,是不允许其他线程读的。

不同点:
1、synchronized原子性、有序性、可见性
有序性只能保证被synchronized锁住的一个块与另外一个块之间的有序,并不能保证块内部指令的有序。
2、volatile有序性,可见性
有序性只能保证变量的读写有序,那么变量构造的顺序无法保证。
3、两者都会插入内存屏障,但是synchronized关键字会在外围在插入monitorenter 和 monitorexit指令,来排斥其他线程,这就是锁。
4、可见synchronized加锁,volatile无锁,所有性能高。

最后一直不明白双重检查单例模式为什么synchronized 和 volatile 要一起使用,现在解开疑惑了:
public class Singleton {
private volatile static Singleton singleton;
private Singleton (){}
public static Singleton getSingleton() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}

synchronized保证多线程之间有序进行,但是在new 对象的时候,初始化与赋值会重排序,另外一个线程来读的时候,可能会读到一个没有初始化的对象。加上volatile关键字以后,可以确保另外一个线程的读操作一定是发生在new 对象的所有步骤完成之后,就不会有对象“逸出”的问题。

以前一直以为是volatile确保new 对象的赋值操作一定是最后一步,发现并不是这样的。

看了一天越看越乱,就暂时这样理解:
synchronized 和 volatile声明之后,被包裹的变量或代码变成一个原子,一个个原子直接会顺序执行,不会交叉执行。只不过synchronized 包裹代码,无法解决变量之间的交叉执行,volatile包裹变量,可以确保变量的顺序执行。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值