Java多线程之synchronized和volatile
来源:互联网 发布:云南旅游市场分析数据 编辑:程序博客网 时间:2024/05/18 18:54
概述
用Java来开发多线程程序变得越来越常见,虽然Java提供了并发包来简化多线程程序的编写,但是我们有必要深入研究一下,才能更好的掌握这块知识。
本文主要对Java提供的底层原语synchronized和volatile进行分析,看看他们究竟干了什么,以及怎么样才能合理的使用它们。
运算速度与IO速度的问题
现代计算机模型,待计算的数据主要存储在内存中,CPU想要对数据进行计算,就必须要经过下面的流程:从内存读取数据-->CPU计算-->把计算结果写回内存。但是,不得不承认一个事实,就是CPU与内存之间的IO速度,要比CPU的运算速度慢很多,所以这样就不能充分发挥CPU的计算能力。
为了解决这个问题,引入了高速缓存的概念,它一般位于内存与CPU之间,与CPU之间有较高的IO速度。高速缓存存放常用的数据,这样就可以大幅度提高CPU的利用率。
由缓存命中问题引出的指令重排序
上面提到了高速缓存之间的概念。既然提到高速缓存,那么就要谈到缓存命中的问题,如果缓存命中率很高,那么整体性能就会提升。所以,试想一下,有下面几行代码:
int a = 0;a = a + 10;int b = 0;b = b + 5;a = a * 2;
通常我们会认为,执行完第2条代码后,a被写入到高速缓存,然后执行对b的操作,最后再从高速缓存中读取出a,再对a做乘法计算。
但是,现代编译器都会对这种情况做优化。考虑一个问题,既然两次对a操作的指令之间,没有使用到a的指令,那么为什么不在对a做完加法后,直接再对a做个乘法呢。反正又不会影响整个代码的语义,而且还能避免高速缓存不命中的问题,万一这之间执行的代码很多,然后a被清理出了高速缓存,那么到最后执行对a的乘法时,又要从内存中读取,这样看来,很不划算,所以就出现了指令重排。就是说指令真正的执行顺序,不一定是按照代码书写的顺序执行的,可能会打乱顺序执行,但是只要不影响整段代码的语义就行了,因为它们是“好像是串行执行”的方式。
比如下面的代码,如果对a的操作真的发生在对b的操作之前,那么就改变了整段代码的语义,所以这时候就不会重排指令。
int a = 0;a = a + 10;int b = 0;b = b + a;a = a * 2;
Java的存储(内存)结构
内存中,主要分为两大块区域:
1、全局区域,所有线程共享该区域。
2、线程私有区域,存放需要使用的全局数据的副本。
存储结构:
第一层,位于CPU内部的高速缓存,为了解决CPU与内存之间IO速度和CPU执行速度之间差距太大的问题。
第二层:内存(包括全局区域和线程私有区域)。
对一个变量的操作,通常需要经过下面的这8个步骤(如果不是同步操作,没有1和8)。见下图的蓝色字。
多线程要考虑的问题
1、内存一致性
某个变量,它在主内存中的值,应该和在线程工作内存中的值是一致的。
2、内存可见性
某个变量,如果一个线程对它进行修改,那么其他线程应该能立即看到它的变化。
3、有序性
如果一个线程A依赖另一个线程B的执行结果,那么在线程B看来是串行执行的指令(其实可能经过了指令重排),在线程A看来,就是一个错误的执行顺序。比如下面的情况:
有可能出现 r1 = 2 、r2 = 0 的情况,因为Thread1可能会进行指令重排,所以对于Thread2来说,就发生了错误。
synchronized工作原理
synchronized关键字,通过第1步,对主内存中变量加锁的操作(lock),获得了该变量的使用权,随后,其他线程没有访问这个被加锁变量的权利,一直到该线程使用完成,然后解锁该变量(unlock),之后其他线程才能访问该变量,当然也能看到该变量最新的结果。
由于synchronized以一种让上述8个步骤原子执行的方式工作,所以,它解决了内存一致性的问题,内存可见性的问题、有序性的问题。
使用场景:所有在有多线程共享数据的地方,都可以使用,简单粗暴,但是会引降低性能。
volatile工作原理
volatile关键字,通俗点来说,就是对一个变量的操作,2(read) 、3(load)、 4(use)这3个操作必须是原子的,而 5(assign)、6(store)、7(write)这3个操作也必须是原子的。
那么我们来看看,它能解决什么问题,因为简单来说就是,读(2 3 4)和写(5 6 7)操作分别都是原子的,相当于CPU每次都是直接和内存交互,所以高速缓存就变得无效了。而高速缓存变得无效,那么基本上,也就没有指令重排了。而由于每次改动都会直接写到内存,每次使用都从内存读取新的数据,所以也就满足了内存可见性。但是由于在读和写之间,其他线程也可以进行读和写,那么还是会出现内存一致性问题的。所以它解决了内存可见性、有序性、但是不能解决内存可见性。
使用场景:由于volatile的特性,主要可以使用在下面的场景
1、不依赖于变量之前的状态的,比如一个 volatile boolean 来在一个线程中控制另一个线程的运行。
2、禁止指令重排,比如一个线程依赖于另一个线程真正的顺序执行结果,而不是语义上的顺序执行(其实是经过指令重排的)。
- Java多线程之synchronized和volatile
- java多线程之synchronized和volatile关键字
- 多线程之synchronized和volatile
- 多线程之 synchronized 和 volatile
- Java多线程volatile和synchronized
- Java 多线程之 synchronized 和 volatile 的比较
- Java多线程之synchronized和volatile的比较
- Java 多线程之 synchronized 和 volatile 的比较
- Java 多线程编程之synchronized 和 volatile关键字
- 【Java多线程】-线程同步synchronized和volatile
- JAVA多线程之volatile 与 synchronized 的比较
- JAVA多线程之volatile 与 synchronized 的比较
- JAVA多线程之volatile 与 synchronized 的比较
- Java多线程之原子性 volatile、atomicInteger、synchronized测试
- java关键字volatile和synchronized在多线程中的应用
- Java多线程中的synchronized、volatile和无锁编程
- java多线程编程关键字volatile,ThreadLocal和synchronized
- 【深入分析Java多线程】(5)synchronized和volatile分析
- BZOJ 3747: [POI2015]Kinoman
- 编写PHP扩展
- IOS IAP APP内支付 Java服务端代码
- 数据结构之字符串ADT
- 支付宝即时到账接口PC和wap移动端的区别
- Java多线程之synchronized和volatile
- HDU 1000 A + B Problem
- okhttp使用,支持断点续传
- 心灵鸡汤之20160429
- 换行符
- novnc的替代品Guacamole
- win10主机如何在校园网的条件下在虚拟机上安装Hadoop并实现远程SSH操控
- Android自动化测试MonkeyRunner(三)------python基本语法
- MongoDB 针对嵌套对象,多层级结构存储,增删改查