本章节主要介绍多线程编程中用的比较多的三个关键词,分别是
synchronized, volatile, final
在开始之前首先要明白JMM模型的内容,即每个线程都是有自己本地的内存,用来存储变量和数据备份的。模型如下图:
具体可参见前一章Java并发编程的艺术(二)——Java内存模型
接下来一起进入这些关键字的世界~
1. synchronized
英文含义就是“同步”,非常的直接明了,就是用来让一些操作能被同步安全进行
问题:如何实现?
技术化说法:JVM通过进入和退出Monitor对象来实现的,查看汇编可以看到同步代码块前插入了monitorenter指令,同步代码块以monitorexit指令结束。
通俗:给存放东西的仓库上个锁,这个锁名字叫Monitor
2. volatile
英文含义为“容易改变的”,就是形容一个变量容易发生变化,但是我要让所有的人都知道它的变化过程。如果所有人都知道它怎么变化,大家不就能愉快的进行交流了嘛,因为大家获取到的信息量是一样的。
技术化表达:不允许自己本地内存存储的数据和主内存中不一样,被声明volatile的变量必须使得该数据同步到所有的线程,不管什么时候访问修改这个变量,所有的线程将立即知道。
问题:如何实现?
技术化说法:通过在volatile写和读操作前后加内存屏障,从而限制编译器处理器的重排序操作。
编译器在生成字节码时会在指令序列中插入内存屏障来禁止特定类型的重排序
当写一个volatile变量的时候,JMM会将该线程对应的本地内存中的共享变量值刷新到主内存中去
当读一个volatile变量的时候,JMM会把该线程对应的本地内存置为无效,线程接下来将从主内存中读取共享变量
通俗:给这个院子(内存空间)前后门(读和写)都上个锁,要求人们只能从前门进后门出,院子里还有个摄像头直播(线程间实时通信),大家都能第一时间看到院子里到底发生了什么。
两个关键字区别:
1.使用的方式不同
volatile是声明变量的,例如:
volatile int x;
synchronized用来声明代码块和方法的,例如
synchronized int getNum(){
return number;
}
2.锁定的范围不同
volatile是针对部分变量,同步线程本地内存和主内存中变量的值,每次修改都让大家知道。
synchronized锁住本地内存,还锁住主内存,每次只允许一个线程执行。
3.开销不一样
volatile只同步部分内容,而且一旦发生变化,其他线程第一时间知道。
synchronized会让整个内存都被锁定住,每次只有一个线程运行,效率低下。
3. final
含义为“最终的,最后的结果”,可以引申为“不再变化的”
技术化说法:它只允许编译器赋值一次,被它修饰的变量值将不改变,和它绑定的对象一旦创建就无法改变引用。
所谓对象引用类似于指向存储在Java堆内的对象的指针。
具体可以绘制出以下的图:
对于int, long等变量,如果加上final后数值是不会改变的(因为只是变量)
但是对于对象这种引用类型,final限定的是对象引用不能改变,但要注意的是对象内存储的数据可以改变(可以理解为只满足始终接触的是同一个对象,与对象内部的数据没有任何关系)
这三个关键字可以说构建了多线程开发的半壁江山,很多优秀的库都是基于对这些关键字的巧妙使用来实现的,懂得这三个关键字就相当于你有了基本的工具去构建复杂的多线程系统:)
更多精彩内容:
JVM学习:
喜欢的话转发分享,关注噢~