【Java猫说】Java多线程之内存可见性(上篇)

代码极光
• 阅读 2199

阅读本文约“3分钟”


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

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

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

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

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

Java内存模型(JMM)

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

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

【Java猫说】Java多线程之内存可见性(上篇)

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

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

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

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

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

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

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

【Java猫说】Java多线程之内存可见性(上篇)

让我们重新回到主题,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多线程之内存可见性(下篇)

点赞
收藏
评论区
推荐文章
blmius blmius
4年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Oracle 分组与拼接字符串同时使用
SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(
Wesley13 Wesley13
4年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Wesley13 Wesley13
4年前
Java 深入理解volatile关键字
我们知道Java中volatile实现了修饰变量的原子性以及可见性,并且为了实现多线程环境下的线程安全,禁止了指令重排。首先我们先来了解一下happensbefore原则、asifserial语义以及数据依赖性,引用自《Java并发编程的艺术》happensbefore简介从JDK5开始,Java使用新的JSR133内存模型
Easter79 Easter79
4年前
Twitter的分布式自增ID算法snowflake (Java版)
概述分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首先他相对比较长,另外UUID一般是无序的。有些时候我们希望能使用一种简单一些的ID,并且希望ID能够按照时间有序生成。而twitter的snowflake解决了这种需求,最初Twitter把存储系统从MySQL迁移
Wesley13 Wesley13
4年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
4年前
PHP创建多级树型结构
<!lang:php<?php$areaarray(array('id'1,'pid'0,'name''中国'),array('id'5,'pid'0,'name''美国'),array('id'2,'pid'1,'name''吉林'),array('id'4,'pid'2,'n
Wesley13 Wesley13
4年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Python进阶者 Python进阶者
2年前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这
代码极光
代码极光
Lv1
觉得力不从心的是因为在走上坡路。
文章
5
粉丝
0
获赞
0