300字范文,内容丰富有趣,生活中的好帮手!
300字范文 > Java如何解决可见性和有序性的问题 – java – 前端

Java如何解决可见性和有序性的问题 – java – 前端

时间:2020-06-21 22:35:16

相关推荐

Java如何解决可见性和有序性的问题 – java – 前端

线程安全有三大特性:原子性,可见性,有序性,只有三大特性都满足的时候才能保证线程安全,三大特性详细描述如下:

1,原子性:通常是指代码执行的效果,要么全部执行成功,要么全部失败;

2,可见性:线程中的本地内存中的变量值,应该立即同步到主内存中,让其他线程可见;

3,有序性:保证代码执行的串行性;

java中的原子性操作主要是使用sun.misc.Unsafe包下的compareAndSwap方法实现,这是sun包下native方法,使用c++代码实现,在CPU层级来 保证底层指令的原子性,或者使用加锁的方式!

但是CAS也会存在问题,具体参见偶的另一个回答/question/6665609704179253507/,这不做 详细讨论!

可见性无法保证主要是因为,内存的存取速度跟CPU相比存在差距,所以在CPU和主内存之间,引入了缓存(线程本地内存)的概念,来增加CPU 的计算性能,副作用就是导致线程操作共享数据时,无法保证中间数据是最新的(有可能还在别的线程的本地内存汇总)!

有序性无法保证是因为基于性能的考虑,编译器和处理器会对操作指令进行重排序,在单线程之中不存在问题,但如果是多线程就可能存在 判断错误的情况!

编译器的重排序也是规则的,java中先天的有序性组成了happens-before原则,如果happens-before原则不能推导出指令的执行次序,则指令 就不是有序的,happens-before八大原则为:程序次序规则(单线程中的顺序性,多线程无法保证),锁定规则(同一个锁先解锁,后加锁) ,volatile变量规则(先写后读),传递规则(类似A早于B,B早于C,则A早于C),线程启动原则(start方法是线程最先执行的方法),线程 中断规则,线程终结规则,对象终结规则!happens-before八大原则规定了指令执行的有序性

由此可以看出,线程不安全的原因十有八九都是追求性能惹的祸!

通常代码在满足三大特性的时候,就能保证线程安全,java中保证线程安全的方式有很多,包括加锁和不加锁,下面来逐一谈下:

1,加锁:比如synchronized(JDK中自带的关键字,JMM规定获取锁的时候,必须清空工作内存中的变量值,保证需要 获取变量的时候,只能从主内存中获取;释放锁的时候,必须把最新值写入到主内存中,这样来保证数据的可见性和原子性)!

还有基于AQS实现的reentrantLock,ReentrantReadWriteLock等都是加锁!

2,不加锁:

①,使用volatile+CAS操作,volatile使用内存屏障来保证变量的可见性和指令有序性,CAS保证原子性,最终实现线程安全,在jdk中Atomic 打头的几个类,都是用了这种方式,实现线程安全!如下图:

②,使用ThreadLocal,每个线程都维护一份数据到自己的本地内存中,相当于没有共享资源的竞争,所以也不会有线程安全问题;

后面会有更多的JAVA开发技术或者遇到的面试问题进行持续分享,希望能帮到更多的朋友,敬请关注。。

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。