今天给大家分享一篇对于理解Java的多线程,特别重要的一个知识点:JMM。在JVM中增加线程机制,首当其冲就是要实现JMM,即Java内存模型。JMM也是大家真正理解Java多线程的基础。
但是大家对于JMM,可以说大多数小伙伴对其的理解是错误的。这篇文章我会从这几个方面讲JMM,让大家获得对JMM正确的、深刻的认识:
1、市面上大家看到的资料是怎么介绍JMM的
2、我会结合一个Linux多线程程序给大家分析,JMM与MESI没有半毛钱关系
3、那为什么要有JMM?不要行不行?
4、Java代码加或不加volatile对于可见性的影响如何理解?
正确认识JMM
有些小伙伴可能第一次听说JMM,大概介绍下:JMM即Java内存模型,不是JVM内存模型。Java的多线程机制是基于JMM实现的。如果你第一次听到这玩意是通过我这篇文章,那恭喜你,你还没有中毒。如果你还想进一步了解,接着往后看。
MESI即CPU的缓存一致性协议。现在取而代之的是MOESI。不同架构的CPU还有可能用其他的协议。理解本篇文章,了解到这个程度就够了,不展开讲。感兴趣的小伙伴可以自行面向百度学习。
你现在百度搜JMM,展示的文章基本都是这个套路:CPU缓存架构、计算机的内存模型、MESI、volatile实时触发数据一致性......不知道这套思路的始祖是谁,把一个简单的JMM讲得复杂得不行,正常人根本就看不懂,甚至被劝退。关键是错的,还能自圆其说地把这些名词串起来讲得像是真的一样。有多少小伙伴有同感的,留言区举个爪。
那如何正确理解JMM呢?一、不要把它与什么CPU、OS内存模型扯上关系。这些区域的数据一致性与JMM无关 。可以想象有一堵墙,墙的一边是OS及硬件,另一边是JVM。JMM只与JVM有关,手没那么长,伸不到墙那边去;二、JMM你可以直接把它理解成工作内存+主内存;三、加或不加volatile跟触发MESI没有半毛钱关系,只是让JVM知道我要不要回写,伪代码如下
假如(该变量被volatile修饰) {
触发回写,让更改的数据写回主内存。
其他线程获取到修改的数据存在一个写内存时长的延时
} else {
不触发回写
}
因为JMM是JVM自己抽象实现的,如果发生了写操作,正常情况工作内存与主内存的数据是不一致的。如果想告诉JVM我需要它一致,就得提供一套机制,这个机制就是volatile的一部分功能。
有两个名词解释一下:主内存,即共享内存,等同于JVM内存模型中的方法区+堆区。工作内存,即私有内存,等同于JVM内存模型中的虚拟机栈。
有些小伙伴可能就要说了:这些都是子牙老师你的个人认识,我们怎么知道是你的正确还是网上的那套呢?是的,没毛病(疯狂记仇)。熟悉我的小伙伴都知道,我要开始上证据了。名声稳不稳得住,就看证据给不给力了。
这个怎么解释
咱们来反证:如果CPU间的缓存一致、CPU缓存与内存间的数据一致需要通过volatile来触发,那操作系统线程间的数据可见性怎么触发?我们会写代码手动触发吗?上一段程序让大家感受下。
代码中没有任何触发机制,大家猜下:线程一sleep前后输出的结果是否都是10?
答案是这次修改是线程可见的。为什么OS的线程天生具备线程可见性,而JMM不具备呢?这个解释起来就比较麻烦了,我会专门出一期内容讲解这个内容。
题外话
总是听到小伙伴说:多线程好难学、多线程学不会、多线程没概念......为什么会这样呢?我觉得可能是这样:大多数小伙伴接触线程都是从高级语言开始的,比如Java。而高级语言的线程是基于操作系统的线程机制实现的,而小伙伴们没有学过操作系统应用层的学习机制,更不了解操作系统内核级的线程机制,所以学完多线程有一种似会非会的感觉。跟别人交流或者面试的时候,滥用应用态切内核态、线程调度等名词。写多线程程序更是惨不忍睹。
我的手写JVM小班新增的内容就是以我所理解的方式让大家精通多线程。学起来会有一点难度,所以如果你没有决心想成为一个技术大牛,建议有这个决心的时候再来,我随时都在。当然啦,底层的学习会比较枯燥,那我是怎么解决这个问题的呢?手写。沉浸在设计者的角度去思考去实现,随着对知识点本身的一点点领悟,代码的一点点成型,那种成就感是CRUD无法比拟的。给大家看下二期新增的知识大纲。
重点来咯!子牙老师亲授,全网唯一的手写JVM小班二期开始招生啦!现在报名享早鸟价。这套课程学下来,如果你真的吸收了,马上领先身边人好几个Level。继续沉淀,多写点作品,简历更加充实,底层实力更加强劲。回过头来学应用层,理解起来特别快,而且理解得比不懂底层的要更加深刻。更重要的是,跟着我学习不会踩坑,能学到实打实的硬核内容。大家看我的视频、我的文章,应该都能感受到,我崇尚的就是硬核。