2

阅读本文约“3分钟”


本文大致讲述两种线程实现的可见性,或许你已经提前想到了,那说明你的基础很好,我们要聊聊synchronized实现可见性与volatile实现可见性。

我们会谈及几个点:指令重排序、as-if-serial语义、volatile使用注意事项等

首先我们要了解下两个名词,有点术语的感觉,虽然我不喜欢那些专业名词,但是你懂得···

可见性:通俗的说就是一个线程对共享变量值的修改,可以及时地被其它线程看到
共享变量:即一个变量在多个线程的工作内存中存在副本,则这个变量就是这些线程的共享变量

这两个名称理解起来还不算难,对吧?那么我们来看看更加专业化的名词,我其实更希望有具象化的有趣的名词来代替,原谅我的文学水平有限。

Java内存模型(JMM)

Java Memory Model 描述了Java程序中各种变量(这里指线程共享变量,你已经理解上面的第二个名词了)的访问规则,以及在JVM中将变量存储到内存和从内存中读取出变量这样的底层细节,就像细胞要在血管中流动一样(一个不及格的比喻),它要求所有的变量都存储在主内存中,每个线程都有自己独立的工作内存,里面保存该线程使用到的变量的副本(也就是主内存中该变量的一份拷贝)

让我们来看看图型吧,文字有时理解起来比起图片要来的复杂,至少我是这样觉得,我更喜欢具象化的说明

图片描述

对于色调我一直有不同于别人的理解,原谅我看似混乱的搭配。

综合的总结一下,结合上图还有之前说的,我们可以定出一下两个原则(或许可以轻松一点的说,而不是用“原则”)

1、线程对共享变量的所有操作都必须在自己的工作空间(内存)中进行,不能直接从主内存中读写
2、不同线程之间无法直接访问其他线程中工作内存中的变量,线程间的变量值的传递需要通过主内存来完成

以上两句可能需要细细体会,就像你喝咖啡后不会立马喝下一口一样,请回味一下。

由此我们可以模糊但又明确的指出共享变量可见性实现的原理

结合下图一同说明下,线程A对共享变量的修改要想被线程B即使看到,需要经过如下两步:

1、把工作内存A中更新过的共享变量刷新到主内存中
2、将主内存中最新的共享变量的值更新到工作内存B中

图片描述

让我们重新回到主题,Java语言层面支持的可见性实现方式:
——synchronized
——volatile

而由以上的篇幅讲解你也知道了实现共享变量的可见性,需要保证两点:

1、线程修改后的共享变量值能够及时从其工作内存中刷新到主内存中
2、其他线程能够及时把共享变量的最新值从主内存更新到自己的工作内存中

synchronized可以实现在于它的性质:原子性(同步性)、可见性

JMM(看到这个单词时或许你应该想到前面的中文含义)关于synchronized有这样的一些规定:

1、线程解锁前,必须把共享变量的最新值刷新到主内存中
2、线程加锁时,将清空工作内存中共享变量的值,在使用共享变量时需要从主内存中重新读取最新的值
(需要注意的是,加锁与解锁需要同一把锁,这让我想到了Redis,你想到了什么呢?)

我们可精华的提升下,即线程解锁前对共享变量的修改在下次加锁时对其他线程是可见的

让我们大致看看线程执行互斥代码(即以上的描述)的过程:

1、获得互斥锁
2、清空工作内存
3、从主内存拷贝变量的最新副本到工作内存
4、执行代码
5、将更改后的共享变量的值刷新到主内存
6、释放互斥锁

(请自己思考一次,不要把5、6步的顺序颠倒了哦)

本文未完~,请期待下篇。
【Java猫说】Java多线程之内存可见性(下篇)
欢迎你留言讨论属于你的见解,毕竟每个人的味蕾都不一样,这杯咖啡有吸引到你吗?
(好像又是一个槽糕的比喻)


本文已转载个人技术公众号:UncleCatMySelf
欢迎留言讨论与点赞
上一篇推荐:【Java猫说】主数据类型和引用
下一篇推荐:【Java猫说】Java多线程之内存可见性(下篇)


Java猫说
1.3k 声望930 粉丝

现架构设计(码农)兼创业技术顾问,不羁平庸,热爱开源,杂谈程序人生与不定期干货。