系列 6:P24:先从一个小程序了解并发 - 马士兵学堂 - BV1RY4y1Q7DL
今天我们从那个最根本的并发开始跟大家聊呃,要聊这个东西呢,我们从我想想从哪开始聊,就就从最简单的一个小程序开始好吧。

最简单的小程序我们开始呃,这个小程序呢。

你跟着我的思路啊,我先带大家读一下。

我们先从小程序开始,这小程序呢大概长这样一个很小的小程序,我们这里有个循环变量,就循环变量名字叫count,它的数值是十个亿,10亿啊,相当于我下面有一个东西呢循环了11次。
好在这里我们有一个class t t,里面有一个成员变量long类型的x啊,就这么一个非常简单的类。

好,接下来我构建了一个数组,有两个t,那么20=6 t ara一等于6t好,同学们看这里相当于呢这是一个数组,这个数组里面装的什么东西呢,装了一个t又装了一个t t里面长什么样啊。
t里面有一个小x小x大概长这样是吧,好接下来呢我们做了一件什么事呢。

往下读,做了这么一件事儿,我们起了两个线程,这是第一个线程,这个这个第一个线程不停的修改,arr 0的x修改11次,第二个线程不停的修改arr一的x修改11次,其实这个小程序就这么简单嗯。
重新用画图的方式解读一下,我们有两个对象,这是第一个t,这是第二个t好,这是第一个t里面的x,这是第二个t里面的x,然后呢我们有两个线程,这是第一个玩命改他这第二个玩命改。

他大概就是这么一个程序来这块没有问题的,同学给老师扣个一啊。

能跟得上的,有点反馈啊,ok懂懂金心,ok感谢,看这里,我们最后呢其实算了一下呢。

很简单,我们算了一下整个程序执行完大概花多少时间。

那好跑一下看。

好看这里啊,这是我们花的时间,这个时间是多少,798,大家记住的时间大概800个毫秒好吧,接下来我对小程序呢稍作修改。

改的地儿不多,就把这两行注释给打开,唉这两行注释打开之后发生什么现象呢,相当于我这个整个class里面呢,我给它加了一些乱七八糟的数据,看到了吧,唉前面给他加了七个long,从p一直到p7 。
在这个x后面呢给他加了又加了七个楼,七个类型,七个long类型的啊,从p9 呢一直到p 15,是不是哎那好,原来这就相当于什么呢,我在用画图的方式说一下啊,相当于什么呢。
相当于哎这个是我们最开始的第一个程序是吧,两个t ok这是第一个t里面的x,是第二个t里面的x,然后有个县城玩命的去改它,第二个线程的玩命去改它,那么我们改了第二个程序之后呢。
相当于在这个x前面从p11 直到p7 ,给他加了好多好多的数据,往后面呢也给他加了好多好多的数据,p9 呢一直到p 15 k相当加了好多冗余数据,当然我们改的重点改的还是这个x,右边这个也一样啊。

我们加了一堆数据,加了一堆数据啊,其他的程序其他的地方哪都没变,刚才呢我们是800多毫秒,我们跑一下看啊,现在新改过之后,这个时间注意看他是244,看了吗,就是最开始的时候是798,现在是244。
也就是意味着什么呢,同样的这个小程序哎,我们给他加了一些乱七八糟数据之后呢,它的效率提升了,来能get到这点,同学你给老师扣个一,好吧啊,稍微给我点反馈,我能知道待会是不是是不是跟上了,是不是听懂了啊。
呃刚进来的小伙伴呢给大家解释一下,那个呃腾讯课堂这边呢他出了点bug,所以呢呃今天呢大家移步移步到咱们自己,咱们自己开发的这个直播间,好吧嗯,哎这就是小程序展现出来的现象,那么这到底是为什么呢。
为什么我们给他给它添加了很多。

乱七八糟数据之后,它反而是效率变高了呢,哎我有我理解上应该是添加这些数据呢,它的效率才会变低才行,你看啊,我把它注释掉,我们就是又回到了那个干干净净的x是吧,好了又是796,哎如果我把这个注释打开。
呃再跑一下,244啊,这就非常奇怪了,这是这个原因是什么呢,在这里呢,这个原因呢其实就是并发的底层里面的可见性,ok下面呢我来给大家解释这个原因,这个原因呢比较特殊啊。

你你要跟着我的思路慢慢走好吧,我们呢从,我把这些线程历史啊,还有面试题啊,基本的概念我就我就不说了,呃我们从呃计算机的组成开始慢慢说啊,听我说呃,我们一般的所谓的这个程序呢,它是怎么执行的。
呃这个事呢比较重要,一般的程序啊,你比方说有这么一个程序叫qq啊,点e x e,假设是windows底下的qq。ex e,他没事的时候,实际上这个文件啊是躺在硬盘上的,对不对,我们把它下载下来。
想让这个qq。ex e运行,我们双击它,双击一个图标,这个qq。e x e的整个程序会放到内存里好了,这就是内存里面的这个qq,点e x c,当然你如果还想运行第二个的话,你再点一下。
还可以有第二个qq,ok对应的是一个文件,说的对吗,那好那这个程序运行的时候怎么运行的,这个程序运行的时候是这样的啊,找到main,不管你是java还是c还是python还是谁,一定有一个主函数。
找到main开始的地方,然后一一句话,一句话的把对应的这些数据放到我们的cpu里面,把对应的指令读到cpu里面,举个最简单的例子,比方说2+3,2+3,这东西是是是怎么算出来的呢。
好当我们读到这这条语句的时候,会把这两个数据二和三唉放到cpu的某个位置上,当然这个位置呢叫寄存器,用来存储数据的呃,好多同学没有学过计算机的一些硬件原理,我在这儿呢稍作补充。
总之呢就是把二和三唉放哪去呢,放到咱们的cpu里面去,好这个二和三其实正常的它在哪啊,是在内存里面是吧,把这个二读过来,读到cpu放放一个位置上,把这三读过来放一个位置上。
然后呢我们对这个23做了什么操作呀,加法操作好,把这个加法指令add o,放到我们的一个叫做program counter程序计数器,其实也是一种指令寄存器啊,大概就这意思就是记着我。
我现在要做什么样的操作啊,是一个ad的操作,这个呃加法操作,然后呢最后就得出来的数值啊,我们得出来的五唉,再给他写回到内存的某个位置上去,大概是这么个过程来,这个过程大概能听明白的,给老师扣个一。
这没有问题吧,总掉线吗,总掉线咋回事,诶首先我我我我的网络还可以啊,因为我这里显示是一帧我都没有掉啊,我一帧都没有掉好,大家看这里啊,那个,这是一个非常简单的程序的运行的原理。
你会发现呢其实所谓的一个小程序啊,呃我们来运行它的时候,其实呢就是呃cpu和内存它们之间的不同,不不间断的不停顿的打交道的这么一个过程,在这个访问过程之中,我们说。
cpu它的速度比内存的速度呢大概快好多好多倍,那大概快多少倍呢,还100倍100倍,有人说这哥们访问一下我的寄存器,如果需要一个纳秒,那么从这哥们这里要是访问到内存去的话,得需要100个纳秒啊。
这是它的速度的差距,速度非常快,那速度非常快的话呢,这就会产生一些问题啊,呃我要是读读里面数据二和三是吧,我读过来我得需要200个纳秒,读一个需要200个对一个,但是我计算一下的话。
我可能只需要一两个纳秒就搞定了,我cpu大多数的时间呢其实在等待啊,这样的话呢对于cpu来讲呢,它就比较浪费啊,我给你提供了这么高的算力,但是你用不上,那怎么办呢,怎么办呢,琢磨一下。
如果我想让从cpu往内存读数据效率变高,诶你猜猜我们该怎么办,兄弟们,课件可以领取吗,可以啊,该怎么办,还挺恶心,技能ok对放缓存这个很容易理解,就是我们在中间啊,由于它离得太远了,速度太慢了。
怎么办呀,哎我们在中间的位置给他放一个啊,离我稍微近一点,他的速度就没那么慢了,然后这里有个二,这里有个三,哎我在这里也放一份二和三,下次啊我要再在访问这个二的时候,我就从这访问了。
我就不会去那个内存里面的,我守着这个距离,如果大家理解不了的话,你把它这个这个地方理解为北京,你把这个地方理解为上海,那么当我们从北京到上海取一份数据,取得比较慢的时候,怎么办,我在济南给他缓存一份。
往这也放一份,我第一次的时候呢,济南什么什么什么什么东西都没有,但是呢我从北京访问上还访问到这个二的时候,往回读的过程呢,我往中间放一份,下次再访问这个二同学们就直接在济南访问了,听明白了吧。
那我再问你,如果从北京到济南,你访问你还觉得慢,你怎么办,还觉得慢的话,中间再放一份,天津在天津再放一会儿是吧,北京到天津,你要还觉得慢呢,各通县一份,ok那那天安门到通县,你还觉得慢怎么办。
跟那个跟那个天门到通县中间是吧,找个找个位置啊,然后再放一层等等,大概就这个意思,那么现在问大家一个问题啊,你们你们动脑筋思考一下,你说呃这一层一层的缓存,我们是多了好呢,还是少了好呢。
兄弟们认为多了好多桥,多,认为少了少了少了,好的桥少多了好少了好,这东西是这样的啊,听我说看情况对,杨大哥说的非常对啊,大家不要认为多就一定是要多少就一定要少,那不是这么回事,多有多的好处,少少的优势。
那多了的好处是什么,多的好处是当我们下次再读数据的时候,它的效率会变高,什么意思啊,比方说我们这里有一层啊,这里有一层,这里有一层,这里有一层,有好多层,由于最近的这一层离我比较近。
如果这个数据我要读的数据呢,最近这层里面有那嗓子速度当然就快多了,但是呢你同学们,你们想一下,如果在每多层的缓存里面都有一份啊,我们想要的这个数据,这个x那假如这个x发生了变化。
那是不是我这个所有缓存里面的x都要改变,想一下是不是如果内存里面的x发生了变化,我这个是不是都要改变,都要变一下,所以呢它维持它的数据的一致性,这块要花费的效率,就花费的精力就比较花费的代价。
就表达能理解我说的意思吗,就是你要保持这些缓存中间的同一个数据的,要保持一致,那你保持一致的话,由于你层数特别多,那当然你花的代价就比较高,这个比较简单,很容易理解,那好这是呃多了的情况,那少的情况呢。
少的情况是我要维护他们之间的一致性代价,我会我会比较低,我改一个地就可以了,比方说只有一层,如果说只有一层情况下呢,又会它的不好的地方在于说诶,他访问的时候的速度也会变慢,所以呃中间到底要放多少层缓存。
这件事听我说啊,中间要放多少层缓存,这件事是工业界呢经过了很长时间的探索,然后呢根据现有的这种硬件的条件,这么来决定的,到目前为止呢,我们绝大多数情况下的,我们的cpu这个缓存的大概是多少呢。
这个缓存大概是三层缓存啊,我把它称之为叫三级缓存,我们略过一些啊,vip里面讲的内容啊,主要看这个缓存的问题好到目前为止呢,我们呃呃我我不在这讲了,我们讲的就是三级缓存啊,再用用用这张图来讲。
这个三级缓存到底什么意思呢,就是从我们的cpu的计算单元啊,或者说从我们的cpu的寄存器吧,在这个位置,那么到我们的内存里面,中间呢是有三级缓存啊,这是第一集,我们称之为叫l one。
这是第二集称重解l two是第三节l three,然后呢内存这个缓存的位置呢,大家不用纠结,如果你没有学过硬件,你可能看着比较别扭一点,实际上这个缓存的位置呢是每一个单独的。
和大家都知道cpu有不同的核,每一个单独的盒都有自己的两极缓存,然后呢,好多盒共用的封装在一颗cpu里面的一整颗啊,两个盒双核cpu,那么它共享一个l3 的缓存,然后cpu直接来访问内存。
大概是这么一个过程,好同学们听我说,当我们有了这三级缓存之后啊,三级缓存的概念来来跟得上的,听明白的老师扣个一啊,我这三级缓存16兆,那不少了,挺挺大的,这块应该没这块应该没问题,比较简单。
那我来考虑一个读数据的场景,比如说啊我们现在这个程序啊,我要需要读到这个内存里面的这个x这个数好,那么读的时候怎么读呢,他读的时候是这么来读的,我首先去我的一级缓存找,我没有找到,那怎么办,去二级啊。
当然有些特殊情况,直接去二级找,就是找不着的情况下怎么办呢,往下级再再再去找啊,l3 也没有,怎么办好,去内存里面读,我读到这个数据之后呢,会顺手往l3 放一份,l2 放一份,l一放一份。
ok那我们下次再用到这个x的时候,就直接从l一可以放完了啊,大概这么一个过程呃,那好我现在再问你一个问题,大家思考这么一个场景,如果我们对每一份微小的数据,每一份微小的数据啊,这么小啊,一个比特啊。
或者一个字节非常少小的一份数据,每次访问的时候呢,比方说我就我这程序非非常特殊,我就是访问了数据一次,ok每一份小的数据,我要访问他的时候都要来这么一遍,第一个找不着找第二个,第二个找不着找第三个。
第三个找不着,ok找到我的位置,y找完了之后呢,往这儿放一分,往这放一分,往这放一分,他效率反而变低了,反而不如直接去访问内存了,是不是这样子,所以呢在这个里面我们还有一个优化的空间。
实际当中呢他是这么来读的,怎么读呢,就是当我用到一份数据的时候,比如说这个x然后我读这个x的时候,我是按照一整块来读啊,我读它一整块的数据x y z是吧,a b c啊,我一下全读过来。
如果这事你理解不了呢,你把它看成一个数组,一般来讲,我们比方比方说我们对数组做一个循环操作,是不是一个for循环啊,从第一个数据开始,然后不停的循环它,那我读我访问到第一个数据的时候呢。
我就把整个数组的数据,一次性的全部给放到缓存里,全部放到缓存里,全部放到缓存里,那就是说我只有第一次的时候,我访问第一个数据的时候,我把这个数据呢放一次,但是第二次访问第二个数据。
马上我就不用再访问第二层三层内存,这个效率就高多了,它的速度会非常快,o这个概念大家是不是能听得懂,就是我读一个数据的时候,并不是说我只读这个数据本身,而是把这个数据放到一整块里。
把它周边的数据一块拿过来,用数组的例子来举的话,就是我们用到的很多种情况呢,呃我用到数组的第一个数据,我大概率会用到这个数组第二个数据,所以我把它读过来,我下次在我离我最近的缓存里去访问。
他的效率就变高了,嗯这块没有问题的,给老师扣个一了,对空间换时间没错啊,比较程序的局部性原理没错啊,就是程序的空间局部性原理啊,当然还有程序的时间局部性原理,这个这个不知道啊。
这个咱们不管他怎么知道是不是第一次,他只要反他,只要他只要看到这个第一级缓存里没有,他就认为是第一次呀,ok大家听我说,下面你再思考另外一个问题,这个问题是这样的,就是我们这个块的这一块的大小是多少。
它大小是多少呃,是大了好还是小了好啊,我们反正我们读一次呢,要读一整块数据,这个这块数据是大了好还是小的好,同学们,来认为大好的敲打,认为小的好的话呃敲小好吧嗯,不大不小适中也看情况对啊,说的很对。
跟那个刚才的层数是一样的,就是大有大的好处,小有小的优势,大有大的不好的地方,小也有小自己的缺点是吧啊,它这个大了有什么好处呢,我们来分析一下,其实很容易理解,如果这个块越大。
那么我一次性能够缓存的数据量就越多,我一次能缓存数据量就越多的话,那我当然命中的概率就比较高,就是我找到我想要的数据里面一下就找到了,就就比较高,但是如果这个块过大的话,我读任何一个小小的数据。
都把它周边的数据全部往里面存存一遍,这个效率肯定会变低,同样的,如果这个块儿过小,存的效率保持一致性的效率肯定变高,这个没有问题,但是呢我的命中率就会变低,哇塞这外面是发生了什么情况。
呃因此在我们同样的啊,也是在我们工业实践的过程之中,找到了一个妥协,在目前为止绝大多数的情况下,这一整块数据的大小,麻烦你给我记住一个数数字,这是数字是多少呢,64个字节来给我记住,不是64位啊。
是64个字节,另外呢给这一整块数据单独取了一个名字,这个名字的名称叫缓存行,缓存行就是我们读数据的时候是一行一行来读,ok它并不是说一个一个来读,而是一行一行来读,缓存行,64个字节,这个概念听懂了吧。
等于16kb大哥,我再给你说一遍,16 64个字节ok了,这个概念叫做缓存行,cline的概念。
系列 6:P25:Java简历中哪两大点最值钱? - 马士兵学堂 - BV1RY4y1Q7DL
呃一般来讲呢,我们讲并发呢有三个非常大的特性,这三个大特性呢呃分别叫做可见性,有序性和原子性,这三个大特性,分别叫可见啊有序,我拿鼠标写不太容易写,好看了,你那个就凑合看,可见性,有序性。
还有一个叫什么原子性三特性呃,我记我用今明两天,这两天的时间,来把这三大特性的最基本的原理,给大家分析透彻,以后,你在简历上写上这块的时候了,唉能做到心里有数好吧,ok,其实我说到这儿呢。
我不知道大家会对于目前为止这个市场上,比较值钱的简历长什么样啊,有有有多少同学是有心里有数的,呃各位同学听我说啊,如果说我们讲一个简历值钱不值钱,它的核心点到底在哪里呢,我提出两个词儿。
你放在心里头去就行了,从开发的角度,基本上就是围绕这两个,就一个是大并发大并发量啊,一个是什么大数据量,各位把俩词记住,就是所有的一切的一切,你的简历值不值钱,取决于你对这两个方面,大概你能掌握了多少。
你项目里头应用多少,你项目经验够不够丰富,围绕大病发量呢,有一系列东西,从底层的到中间件的缓存的,消息的分布式的,然后微服务的啊,然后高弹性的高扩展的云原生落地的是吧,这是这是一系列。
那么围绕围绕大数据量呢,就是大数据量的存储是吧,处理计算清洗啊,包括挖掘等等这一系列,各位能能能听明白吗,尤其是这个对于有有经验的同学来说啊,请你抓住这两大核心好吧,一个叫什么,一个叫大并发量。
另外一个叫什么,另外一个叫大数据量。
系列 6:P26:缓存一致性协议 - 马士兵学堂 - BV1RY4y1Q7DL
呃我相信网上的好多,好多同学呢应该读过和缓存行有关的,一系列的方方面面的文章,呃各位同学读过的,你给老师扣个一啊,看有没有读过的,一直不清楚核到底是什么,如果你想透彻的理解的话,得去学那个数字电路。
但是你简单理解的就是,用一个用来计算的单元就可以了,嗯64x8比特对,64个字节啊,好当你理解了这件事情之后,呃,我们来考虑就是这么这么一个场景啊,现在啊,假如说这是我们内存里面的各种各样的数据。
它是呃从逻辑上来说呢,它是一行一行一行一行的,对不对,那在某一个行里面呢有两个数据,一个是x,一个是y,那好假如说啊我们现在有一个线程,那么大家都知道这个线程是跑在不同的cpu上的。
也就相当于呢我有一颗cpu,需要去读这个缓存行的数据,那么读出缓存行数据的时候,他是怎么办的呢,他首先是在一级缓存找,没有二级也没有,三级也没有,怎么办,ok在我们的内存就找到了。
注意这哥们儿要访问的是这个x啊,这个面好,这个x,但是呢他会顺带着把一整行数据全部读过来,ok它这次呢l3 里面把一整行数据缓存在这,把l2 里面缓存一份,l一里面缓存一份,然后呢我以后对于x的访问哎。
就直接从这里开始访问了哦,很简单,那与此同时啊,有另外一个cpu或者说另外一个线程,它要访问的数据是谁呢,他要访问的数据是y,他首先在这个里面找有这个y吗,没有二级里面也没有。
他去三级找三级里面有没有这个y啊,有三级里面这个y在哪个环,在哪一行数据里面在这一行,所以它会把这行数据放到这,把这行数据放在这,然后呢他就开始访问这个y是吧,这边就开始访问这个x大概这么一个过程来呃。
我们这个图呢比较容易看懂啊,那么在这里呢大家伙你瞅一眼,其实对于同一行数据,同样是这个xy所在这一行的数据,它有多少个备份,你看一眼啊,一共有多少个,同样的好备份好,一共有多少个。
一个两个三个四个五个六个,ok那我就想问你了,如果在我们这边这一行的数据发生了改变的话,我要不要使用某种机制去通知另外一行,说哥们儿,我这行的数据发生了改变啊,你这边有及时变化啊,你这边有及时。
你你要不及时变化呢,你访问到的就是没有更新的原来的老数据了,就可能会出问题,原来这个x是零,然后呢我已经把它改成100了,但是呢由于你读的比较早,你这个x还是零,现在我通知你,你得更新一下,哥们儿。
你得更新一下,不更新的话,你这个x的值就是老的值,有可能会不对,咱这块儿听懂的,给老师扣一没有问题吧,也就是说我们需要某一种机制保持什么呢,保持住我们各个缓存行之间的数据,让它保持一致,就是出香之间的。
你不要有错了的现象,ok都保持到一个最新的状态,所以我们讲每一个不同的硬件,每一科不同的cpu啊,不同的厂商都有自己的这种协议,来保障不同缓存行之间的数据,来让它保持一致,避免用到的时候会出错,好了。
这个协议呢我们把它称之为叫缓存一致性协议,叫缓存一致性,ok我们带来这个概念叫缓存一致性协议呃,有多少同学听说过缓存一致性的,有没有缓存一致性协议,这个协议里面有常用的协议有哪些,有听说过。
你敲出来有没有酒吧,有没有听说过这个词,就是m e s i是吧,网上一搜应该一大堆,这是什么,这就是英特尔,英特尔cpu经常使用的,英特尔cpu的使用的那种缓存一致性协议,呃,但是呢需要各位同学听清楚。
就是不同的这种cpu,不同的硬件厂商有他自己的缓存一致性协议啊,有的是英特尔的m s,有的使用是m s m o s synfirefly,dragon protocol等等。
这些东西呢都是现有的cpu厂商所提供出来的,方方面面的这些个缓存一致性,所以大家不要把m e si等同于缓存一致性,这块就就行了,呃,vip课程给大家详细解释了呃,至少是粗略让大家入门什么是msi。
但是在这呢我就不展开了,那个呃各位同学听我说啊,网上有很多很多文章啊,就是把msi和volatile等同起来,还有呢把m e s i和缓存一致性协议等同起来,我告诉你这些全是不对的。
全部队没有一个是对的,听我说,缓存一致性是一个普遍性的称呼,具体的协议,每种硬件厂商和每种硬件厂商它不一样,这块能理解吧,嗯ok好看这里看这里,那那么我们回到刚刚那个小程序好吧,当你理解了这件事之后。
我们回到刚刚那个小程序,就这个小程序呢你会发现诶,我把这个x前后给他堆了一堆的数据之后,大家大家稍等我一下啊,我让咱们外面的咱们的小姐姐们小点声音啊,我马上回来,这边说了放小声,说把声音放小了。
说话声音放小,ok ok好,看这里老板生气没有生气啊,看这里看这里,就是呃,当我们把x前面给他堆了一堆的数,冗余数据之后呢,我们的访问效率反而提升了啊,这个原因到底是什么呢,我们来分析一下。
这是那个第一个x,这是第二个x呃,然后我们这个这个这个线程呢,使着劲或者这个cpu的使劲使劲去访问它,这边呢使着劲儿去修改它,那么在第一种情况下,我们x前后都没有什么数据,s前后没有什么数据呢。
这俩哥们儿大概率啊,我们不能说所有的概率就是大概率位于同一行,这个没有问题吧,那为同一行就意味着什么,意味着说诶我这边呢会有这行的缓存,你这边呢也有这行的缓存,那我改完了这一行的某一个数据之后。
我只要改了其中一个呃,我肯定得用使用某种机制啊,不管这种具体的机制是什么,我一定要通知另外一个线程,或者叫另外一个cpu是吧,就是我这边改动的东西,你那边得可见可见性是不是哎我这边改完了。
你这边得得可见k采用什么样的机制,让你可见是立刻可见还是固定时间,而在什么情况下触发你可见等等,不管怎么样,我得我得使用一种协议来通知你,你想我这边改了10亿次,那我就来回来去老通知你。
我这边改了改了改了改了啊,然后呢,你这边改了第二个x也得来回来去老通知我,所以当我们这个线程开始不停的修改x的时候,看上去他们两个互相之间不影响,但其实他们在内部在不停地使用缓存,一定的协议来保持一致。
那么为什么我们在它前后加了很多数据之后呢。

它的效率就变高了,原因是什么呢,其实也非常简单,同学们,你们想一下,如果我把这两个x想办法,让它位于不同的缓存行里面,诶我前面给他堆一堆数据,后面给他堆一堆数据,然后呢这个x前面也是一堆。
后面也是一堆好了,这哥俩呢由于是这个长中,中间的长度,已经超超过这个这个这个缓存行的长度了,所以呢哎这个x位于这行,这个x位于这行,那么同学们你们想一下,我这个线程去访问他的时候,这个线程访问它的时候。
我们还需要互相通知吗,待会不位于同一行数据啊,所以你这边缓存只有这个x,你这边缓存只有这个x,没有其他了。

在这块听明白了,也老是扣个一,所以就相当于什么呢,相当于我们用了前后的冗余数据,把这个把这个把这个数据给它保护起来了,保护在一个单独的缓存行里面,是不是很简单嗯,所以它的效率就提升了啊。
这就是我们这个小程序,你把它注释打开之后,它效率提升的根本原因啊,前有同学老师理解不了这一点,所以那个我给大家画了一小图,这个小图一看就能理解,大家还记不记得还记不记得,就说我们一个缓存行的大小是多少。
是64个字节,是不是那好啊,我们一个long类型,java里面的long类型是都是多少多少啊,是八个字节,是八个字节,那64÷8,也就是我们统共一个缓存行,里面能装多少个long呢,一共有八个。
所以我在这个long前面放七个,这个long前面的这个long后面放七个,那么你想想看,无论你如何的排列组合,不管你这个缓存行到底你是你,你是怎么划分啊,你是从哪开始划,无论你怎么画。
哪怕你从这儿开始画,ok我跟前面组合成组成一行,或者从这儿开始画,我们后面组成一行,或者从中间开始画,把我放在中间,但是你放心,我绝对不会和另外一个x位于同一行,以前有同学说啊,以前的同学老问说。
老师啊,你为什么在前面放七个,后面也放七个,你你你你都放前面行不行,我做了一下实验,我发现只要前面放了就可以,是的这个是没问题的,你你把这个前面放了呢,也有可能和其他x不为同一行。
比如这个x在这个位置是不是,但是你要小心的是,有可能这个x位于这个位置,那么它和后面组合的时候,这俩哥们又位于统一好了是吧,同学们乘有两个x吗,乘有两个,你有一个数组啊,你仔细读那个程序就知道了啊。
它是它是一个数组啊,我是用我我是做了做了一个一个数组,这个x这个x啊做一个数据,ok,这就是为什么呢,我们在前后给他怼上这些冗余数据之后呢,它的效率提升的原因,那有同学可能就会说了。
说老师这这这我我要编程,我要考虑这些东西,我就累死了,对同学们听我说,你们日常要编程,要写成这样,我估计你们你们老大他他他会骂,你看不懂是吧,你在干嘛,嗯嗯所以这个东西呢大概存在于两种场景之下。
第一个场景呢,是你真的在为整个公司的人在做类库,在在写底层啊,你写的这个东西呢,你们整个公司的人都在调用,你要想办法提升你效率,可以考虑使用这种技巧,第二个什么呢,第二个是面试。
这是这这这对我们来说是有意义的,以前老有朋友问,说老师讲了半天,这些东西我谁都开发也用不上,我学了干嘛干嘛呢,很简单,过面试最简单的逻辑就是你过了面试拿25000,过不了面试,一分钱拿不着。
或者说你答出来这道题答到这个深度了,拿25000,你达不到这个深度,虽然也让你过了,拿15000,中间差1万块钱,这就是它的存在的意义,好多同学不理解,为什么大厂里面考的东西越来越深,很简单。
因为考浅的,已经不能够把大家伙的水平给区分开来了,考一个招一个切土豆的,一来来了1万人,我怎么切啊,没法切啊,我都不知道该选谁,怎么办,来加强点条件啊,了解那个刀的结构的优先嗯,刷刷去9000。
还剩1000人,还是太多,了解土豆营养的优先,听懂了吗,嗯这就是它存在意义好吧,那么真的有真的有人是这么写程序的吗,呃其中呢如果你是jdk一点七,你要读过源码的话。

那个lb q就是link的blocking,kj u c里面的那个类,他就是这么写的,如果你还理解不了的话,那我再给大家介绍一个小的框架,这个框架的名字叫this rapture。
这框架不知道大家有没有听过。

那要听过这个框架呢,你给老师扣个一distrutter,嗯然后找人来做核弹的切土豆,那没办法,做核弹的太多了。

潇洒廉价扣二其他人呢有没有人听说过是吧,有事没事的,多去了解方方面面的各种各样的框架,呃,这个discharter这个框架呢曾经获过一个奖,叫杜克奖,这个奖项相当于计算机械的奥斯卡奖。
你可以这么来理解就行了,它是英国的一个做交易的公司,l max所开发出来的,它可以理解为最,效率最高的嗯,最快速度最快的单机版的mq n q message q,message q呢就是消息队列啊。
一般来讲呢你可以把它简单认为是一个数组,然后呢我一个一个消息扔进去,有一大堆的线程往里头扔消息,还有一大堆线程呢从把这消息读出来,ok这就是一个mq,一个mp呢,大概率的同学们会认为它是一个数组来构成。
或者链表来构成啊,总之呢就是一个线性结构,但是呃这种呢一般有一个头指针,有一个尾指针是吧,我们新新来的呢,哎把这个为把把往往尾巴上放是吧,读的时候呢,你按照按照自己的规则来读,那好呃。
我们这个disrupture,它采用的这种结构呢和其他的不太一样。

它是一个环形结构,它一个环形结构,一个环在这个环上面分布着一系列的存储位啊,二的多少次方,然后我们最开始的这个指针指在零零,或者叫指在-1这个位置上啊,然后诶等我们向下挪一个,他就指到了零这个位置啊。
指到一的位置,指到二这个位置,我们指针指到哪个位置,你就往哪个位置上放东西就行了,哪有东西是我放满一圈了,怎么办,放满一圈呢,你就等着这个零被毒走了,被释放了,你就再往里头放就可以了好了。
这个玩法呢它就只有一个指针就搞定了,它不需要一个头指针,一个尾指针需要两个指针,只需要一个指针就搞定了,ok那这个这个玩意儿呢,就嗯这方面呢它提升了一个效率,它其实提升效率有两个大的方向啊。
一个是c a s,这个明天我讲给大家听啊,就是做这种无锁的等待嗯,cs然后呢,其中还有一个就是使用了我们这种catch line ping,叫缓存填充技巧,o看这里,那么他是怎么做的呢。
好我们来读一下这个指针它的源源码,大家可以想象一下,就是这个指针的话,它一定是好多好多线程去访问它,因为好多线程都往里扔吗,好多线程去访问,他,要争取对这个这个数据要进行保护啊。
要不要和其他数据混在一起,不要和其他的有用的数据混在一起,这个呢万一其他数据改了,他们两个位于同一行的话。

还得来回来去的进行这种缓存行的一致,效率就变低了,这个东西呢它是追求效率的,框架名怎么拼,没记住这个啊。

给大家打开这个框架,disrupt。

闪电啊,你如果你如果没记住的话。

那你就记这个闪电就可以了啊,英文的闪电像闪电一样快,不是那个疯狂动物城里面的闪电。

好,我们来看看在这个destruction它的框架里面呢。

有一个非常好玩的东西呢,叫做ring buffer,ring环形的buffer,环形缓冲区,也就是我们刚才画画的那个环形的那个,纯消息的地方。

好我们点开这个rain buffer,看看它的源码,你会发现很好玩这个东西,这是那个指针最开始的位置-1啊,然后呢你会发现在这里放了七个lp 1,一直到p7 ,好吧好不好玩,是挺好玩的。

p11 直到p7 ,如果你没有听我讲过,你一定不知道这玩意儿是干嘛使的,很简单,它就是保护那个指针的,不让那个指针和其他地方位于同一行,这样他的访问对那个指针的访问的效率会提升,o来这块能看懂的。
给老师扣个一,是不是有人这么写程序啊,他这应该没问题啊,阿里的基础职位嗯,那有同学说老师这不对啊,你你你只在一边放了呀,只有一边有p到p7 啊,这个不对啊,我们我们刚才讲你要是有一个数据的话。
你得给他放一边,你这x你这个它放在这边,那右边有可能和其他的这种有用的数据。

组合在一起啊,那不对是吧,应该还有还有一个p一到p7 才行才对,那么它在哪呢,看这里rain buffer的父类叫rain buffer fields。

我点进去ring buffer的父类叫ring buffer pad,专门用来做填充的。

好在这里又有一个p一到p7 ,所以你可以想象一下,这个x前后都有p一到p7 的保护,那么是不是访问它的效率。

在高并发的情况下就提升了,来这块听懂的给老师扣一有没有问题,这就是最简单的最基本的一个可见性,但是它和程序的可见性还不太一样,它和语言无关,不管你是cc加加python java。
随便你在它最底层的运行上,就是必须要保持缓存行的可见性,o挺好玩的是吧,嗯好当你理解了这件事之后呢,算是开开个胃啊,上点开胃菜,大家呢先理解了一点这种最基本的东西,下面呢我们来讲深入一点的。
稍微难一点的好吧,呃下面这个东西呢我们把它称之为叫做有序性,呃,可见性相对简单,其实就是各个缓存之间如何保持一致,这个改了之后怎么通知对对方好了,这个可见性,那么概念上相对简单啊。
那么第二个呢这个要稍微麻烦一点,这个东西呢叫有序性,打起精神认真听,就是我们写程序的顺序啊,程序执行的顺序。

有同学说了,老师你这还有什么可讲的是吧,好同学听我说这个小程序呢,我先让他跑起来,因为这个小程序出结果会非常的麻烦,非常难,有可能最长的时候我等了呢,哎运气真好啊,这次这次只是2万多次,我就得到了。
结果我有一次等等,等,他的话等了十几分钟才得到结果啊,好看这里啊,我给大家解释这个第二个小程序,这个小程序呢比第一个小程序稍微复杂一点点,你要认真听啊,不要,不要走神,不然很容易跟不上啊。
听我说这个小程序是这样的,我们有四个数据,这次数据比较多,x y a和b这是我们四个数据,一个是x,一个是y啊,一个是a一个是b啊,最后我们考察的是这两个x和y的数据,a和b呢就是用来做存储用的啊。
一会儿往下看就行了,然后呢我们进行了一个大量的循环,基本接近于无限次的循环,接下来我在每一次循环里面,每一次循环里面我干了一件什么事呢,起了两个线程,这是第一个线程,这是第二个线程。
好第一个线程干了这两句话,a等于一,x等于b第二线程干了这两句话,b等于一,y等于a就这么简单好,我现在呢需要大家说考察的是什么东西啊,就是这是第一个线程干的两句话,a等于x等于b。
这是第二个线程干的干的两句话,b等于一,y等于a,我现在如果说这是第一句,这是第二句,这是第三句,这是第四句,我现在想问你的是,我们,正常的理解,按照正常的理解,这四句话在时间上先后执行完的顺序。
会有哪些种情况,比如说我第一个线程执行完了,第二个线程才执行,我们执行的顺序就是1234,当然还有一种可能性是第一个线程执行完了,第一第一个第一句话,然后第一个线程暂停了,轮到第二个线程执行。
第二个线程的执行了第三句,然后轮又轮到第一第一个线程,第二句1324能能明白我说的意思了吧,兄弟们,就是我现在要考察的是,这四句话的前后的执行的顺,序的组合到底有多少种。

这个大家知道吗,这个组合有多少种啊,就是这个前后的顺序的组合有多少种,这个不不不一定啊,四种no no no,没那么简单啊,这个我就不考大家了。

排列组合是吧,比较麻烦,你得自己排。
系列 6:P27:并发编程底层原理(一) - 马士兵学堂 - BV1RY4y1Q7DL
大家看这里就行了啊,看左边基本上就是这么几种啊,这是第一句,第二句是1234或者叫3412是吧,这种情况啊,1324对吧,嗯3142ok或者1342是吧,然后呢3124大概基本上是这123456。
一共有六种,一共有六种啊,基本就是六种,基本没有其他情况,ok组有六种,看看明白这意思了,兄弟们,那好我们要考察的是什么呢,在这六种执行的情况下,六种不同的情况下,我们最后得到结果。
得到的x值和y的值到底是多少,好我前面讲的能跟上的,你给老师扣个一啊,我讲的是每次讲的是小程序呢,由于它稍微复杂了一点点,好多人跟跟不太上,给我点反馈好不好,所以对,那我们考察这六种情况之下呢。
最后最后得到的x y的结果到底是几,反正的初始值都是零,你看啊a等于一,x等于b那这个时候x的值呢,由于b初始值是零,所以x等于零,b等于一,y等于a,由于这个a已经变成一了,所以y变成一。
所以最后我们得到了xy的结果,这个是零一,以此类推,这里得到的是一零,其他所有情况,像这种的得到是一一这种的一一,这种的一一这种的一一,那就意味着,不管他们之间无论进行任何的排列组合。
不管你怎么进行排列组合,随便你最终我们会得到三种可能的结果,第一种是零零,第二种是什么零一,当然啊一零是吧,还有呢啊对不起,没有零零啊,sorry对不起,三种可能的结果,第一种是零一是吧。
第二种是什么一零,第三种是什么一一,就这三种不可能有,第四种不可能有第四种,各位听明白了吗,好讲到这儿还能跟得上的,给我点反馈,给老师扣个一啊,呃但是呢你会发现,如果你去执行这个程序的时候。
这个程序的什么时候会结束,只有在x和等于零和y等于零,这两个x就是发生了零零这个情况的时候,x y同时为零,我们最后这个成绩才会结束,你会看到我第23841次循环,我们得到了零零这个结果,好不好玩。
那这事就很奇怪了,按理说是不可能的,为什么他会有零零呢,why,神奇密斯卡对,因为发生零零的可能性呢其实并不高啊,不太容易发生,所以有的时候我们一一个程序,一个程序要跑下来的话呢。
有可能你要等好长好长时间,才有可能会发生零零啊,像我第一次运行2万多次就出来了,这个是属于人品好,你像这个第二次在运行,你就等吧,也不知道要等多长时间了,就我们就不等了啊,我们一会儿先讲着课。
然后一会儿你就会看到它的结果,好吧,呃为什么会这样啊,同学们,我们来分析一下在什么情况下才会出现零零,因为你按照排列组合来说,你这种情况是不可能出现零零的,对不对,想出现零零的,基本上是这两种情况。
注意看基本上这两种这两种情况,这两种情况是什么呢,首先是交叉执行,写这个这是第一个啊,第一个本来是a等于一,x等于b是这这是112这个顺序,结果它变成了二一先执行了,第二句话,x等b后执行了a等于一。
ok这时候x等于零没问题,同时呢与此同时第二个线程也反过来了,知道吧,原来是三四的顺序变成四三,先执行了y等于a后执行了b等于一,或者反过来或者反过来交叉,无所谓,能看懂吗。
就为什么这种情况下会非常的稀有稀少,是因为我们不仅第一个县城里面,两句话的顺序变了,另外的第二个线程里面,两句话的顺序也同时变了,并且两个是交叉执行,ok在这种只有在这种可能性之下。
我们才能得到零零的结果,o,好了,这次我们是循环了,你看啊,这次我们是循环了多少次,是27万,这次运气就不太好是吧,循环了270775次之后,我们才得到了零零的结果,ok那也就意味着一点。
我们其实就就就就先先不用讲各种各样的理论,我们从结果来看就已经推算出来了,你看到的不一定是你看到的,你看到的这两句话,a等于一。

是吧,你看到的这是第一句话,然后x等于b。

这是第二句话对吧,你看到的是应该你理解应该是先执行它,后执行它,但是未必有可能是先执行的,x等于b后执行的a等于一好了,这个东西呢就是我们讲的程序的有序性,一般来讲有序性只有一个线程,不会造成严重后果。
没有没有后果,只有一个线程的情况下,你有序性你打乱顺序没有关系,按照规则来就可以,但是在我们多线程的情况下,县城之间的有序性会造成各种各样可能的混乱,而且这是面试的一个重灾区。
我们先讲清楚了这个有序性的概念,来这块听懂的,给老师扣一,我们再来,我们先讲几道和有序性有关的面试题,你们来体验一下,就是说有序性有关的面试题大概长什么样好吧,怎么保证线程执行顺序,我一会儿给大家讲。
比较简单不难,我们呃看一个看看看看看看看看这个吧,看这个呃这个小程序呢,那他的你可以考你的是要找bug好吧,你看看这个小程序有有什么bug存在,这小程序的模拟是一种什么情况,是这么一种情况。
当某件事发生的时候,我要干什么,这个说起来容不容易解释,你可以读成语就行了,我们假设有一个number值,这个number值ok我我我我媳妇的年龄是吧,他现在还比较年轻。
什么时候这个number值变成18了啊,我就娶她为妻,大概这么一个情况呃,我先回答第一个问题,就是这个private static int number,我没有给它设设初始值啊,没有给它设初始值。
同学们告诉我这个初始值是几,number的初始值是几啊,兄弟们,这大家知道吗,这个应该应该知道啊,这个必须得知道,对不对,哎是零,没错啊,说的很对啊,这number的初始值是零,那好。
然后这个是我我媳妇这年龄什么时候准备好啊,我用我用另外一个值给它做一个监控,这个值呢叫ready类型是word类型,布尔类型的ready等于false,什么时候呢,它变成了我能娶她为妻的这个年龄好了。
这个时候这个ready就变成true就可以了,所以呢我在这里有一个观察者线程,readers threat,在这个观察者县城里面呢,我就去读,well,not really,只要你还没有变成true。
well,not ready si yield,我就我就不停的循环不停的循环,什么时候你ready变为true了,我就把这个number值给你打印出来,表示啊,这件事发生了。
那好在我们的主线程里面启动读者线程,接下来把number设为42啊,42我们就结婚是吧,ok然后把reading设为true,那么大家就可以想象一下,number呢已经设为42了,看这里你可以想象一下。
number这个值已经变成42了,然后这个ready已经变成true了,我这个线程一直监控这个ready是吧,这ready已经变成true了,好了,我就开始去访问这个42了,ok大概这么个情况好了。
这个小程序呢呃有两个bug,其实这个小程序有两个,有两个bug,你们找出其中一个来就可以好吧,同学们告诉我,这小程序什么地方有可能会出bug啊,看看是不是能够找出来,54321顺序对,顺序没错。
咱们刚刚讲完有序性吗,好我就问一句啊,就是同学们你们想一下,这是两个线程啊,啊这是第一个线程,对不对,这两个线程啊,这这是这是第一个线程,然后呢我们的主线程是第二个线程,同学们,你们想一下。
我这两句话就是number等于42和re等于true,这俩哥们我刚才讲了两句话,有可能换顺序的,那有没有可能这个ready等于true,放前面去了是吧哈哥俩换了个顺序,ready等于true放前面去了。
正好还没有来得及,就稍微的给大家解释一下啊,这是那个reading等于true,本来正常情况下number等于42,你理解他是在在在在这个前面执行,是不是,但是不好意思啊,这俩哥们换了个顺序是吧。
那等40跑下面来了,reno一处跑上面去了,就跟刚才我们看到的那个那个那个语句一样,他俩换了个顺序,那么在这种情况下,ready已经设为true,还没有来得及去执行,number等于42的时候,好。
我们这个线程执行were not ready,哎ready已经为true了,那就那那就继续往下执行了,直接把number值打印出来,注意这时候number还没有14,42呢,等于几啊,输出为零。
这就不是我们想要这个结果好吧,为啥会换顺序,一会儿我讲为啥我们已经观察到这个现象了,o所以我们可能会得到零这个结果,这明显不是我们想要的,o这是这个小程序的第一个办法,就是它的有序性造成造成的啊。
总之有序性这件事你就一定要知道,就是在我们的单线程的两句话,他前后有可能会换顺序,注意是有可能呃,在这呢我我我先给大家讲为什么会换吧,好不好,先给大家讲为什么会换顺序啊。
我先讲为什么一会儿再来继续讲题好吧,认真听,为什么两句话有可能会换顺序呢,其实呢答案非常简单,是为了优化,是为了提升效率,所以我们计算机的很多地方,都是为了提升我们的计算效率。
ok我们假设有这么一种场景,这么一种情形,认真听呃,我呢有呃做了有有我cpu有有两条指令要运行啊,这是第一条指令,第一条指令呢是去内存里面读一个数据,回来漏的一个数据啊,low的一个数据。
注意去内存里面读,那么第二条指令是什么,这第二条指令是我在我自己的本地的某一个,寄存器里面做一个加加操作啊,比如爱佳佳这个i呢已经在我寄存器里面了,同学们,你们想一下啊,我们假设一下。
我们假设我们从呃我我我是我,我前面刚刚讲过,说这个cpu访问内存的时候呢,它的速度比较慢,我们假设读着读,把这个数据读回来,我们需要100纳秒,好,我们做这个艾加压操作呢,他正常的其实它速度非常快啊。
正常就几个纳秒,我们假设吧假设需要20个纳秒啊,假设好,如果说我严格按照两句话的前后顺序执行,严格按照一二,严格按照前后的两句话的顺序执行,那么总共要花费的时间是120个纳秒,能看懂吗,这个应该没问题。
但是呢如果说我去发了一条读指令过去,我让这个这个读去读内存,我还没有来及读回来,这个过程之中,反正我等着也是等着我这100个大表,等着也是等着我在这个时间呢,先把这个爱加加操作给他先做了。
放前面去先做了,明白了,就是我们还正在执行这条语句的过程之中,还有另外一条语句,执行完了,我们最后得到的结果是多少呢,注意是100个纳秒,我们节省了20纳秒,来这块能听明白的,给老师扣一。
如果你还理解不了这件事,我给你给你举个最浅显的例子,比方说我我现在要吃饭,我吃饭的话,那我肯定要做锅做水,然后烧菜洗菜切菜,然后呃蒸米饭等等,有一种呢就是说我我我先跟着做锅,刷锅做水是吧,然后等水开了。
我再我再去切菜,再不弄弄,这个太慢了,怎么办啊,在等水开的过程之中,我就把菜给切了,把那个其他的全都给准备好了,把我锅碗瓢盆全全给摆好了,ok这个效率就提升了,就这么简单,所以它的本质上是为了优化。
那好那好,那当你那个理解了这件事之后,还有同学可能就会说说老师这能随便换吗,他有可能会换,但是他能不能随便换呢,答案当然不行,某些情况下两句话是可以换顺序,但是某些情况下是不能够换顺序的。
给你举个最浅显的例子,就是前后这两句话存在依赖关系,比如说我们现在有这么一个这两句话,i等一爱佳佳来,你告诉我这哥俩能换顺序吗,能不能,这百分百不能,这哥俩换了顺序,我们最后得到的爱这个结果。
i是内存里面有个值,它它它它就不一样了,对不对,i等于i加加,我们得到的结果是二,我们要反过来的话,a加加a等于一,我们得到的结果是一,所以这两句话叫做存在前后依赖关系,那这两句话就不能换。
这东西由我们的优化程序来判断编译器,来进行判断编译优化啊,呃当然执行的过程中也可能进行优化,来这块能听懂的老师扣一单线程不会,no no no no no no no,注意所有的有序性指的就是单线程。
单线程里面才存在有序性,多线程有序性是正常的,多线程乱序是正常的,兄弟们,你多线程两不同,不同线程能连这几句话,但那那这个顺序是我是确定不了的,这就是单线程的两句话,他们的乱。
他们的乱序才能称之为叫叫有序性,ok但是单线程的有序性啊,呃按照规则来的时候,他不会对最终最终结果产生影响,只不过在多线程的情况下,他才会产生影响,这个比较麻烦,我再说一遍,我再说一遍啊,这里有点绕。
就是单线程,比方说这两句话,i等于1g等二,这两句话可以换顺序吗,随便换是不是,反正最终的结果呢都是哎,这里面这个i的值和j的值,最终结果都是一样的,随便你放前面,放后面都一样好了,所以在单线程里面。
这两句话是随便换顺序的,o但是多线程就像刚才那种情况,就有可能发生我们不可预知的情况,有可能发生我们理解不了的情况,ok所以单线程的有序性是为了提升效率,但是它会造成多线程的麻烦。
来各位听懂的给老师扣一,那我们讲讲最简单的这个换顺序的原则,就这两句话,怎么样才能允许他换,怎么样不允许他换呢,其实最终的判断是单线程的最终一致性,就是你只要最终的我们得到这个结果是一致的,你就随便换。
a等于对等,二,你谁谁在前,谁在后,我们得到的i和j的值都是一样的,但是最终一致性不能保证的情况下,你就不能换,这时候这是最终的一个一个判断,当然里边的判断需要各种各样的技巧,他的技巧比较多。
那是编译器干的事,你可以暂时不用理解它,好简短总结,为了提升效率,单线程可能换顺序,但是换了顺序之后,在多线程的情况下可能会惹麻烦,这就是我们要讲的最基本的概念,呃我讲到这儿还能跟上的,给老师扣一。
我们下面要上一点难度啊,我希望你前面都能理解了好吧,因为前面这些个呢我们讲只是一个偏便宜点,说明是1万多的难度吧,后面我们可能要上到三四万左右的难度啊,这个难度呢就更难一些,注意看呃。
我们来看我们来看这件事,也是多线程的情况下引起的啊,多线程情况引起的,这是一个著名的问题,这个问题呢叫做this escape,this escape,this指针溢出问题。
这个问题在所有的语言里面都有,各位同学,我其实今天讲的和明天讲的很多东西,都跟语言的关系并不大,它涉及到的都是一些底层的原理,这个是this溢出的问题,这个指的是什么东西啊,我们理解这件事情呢。
我们需要你理解,就是一个对象的构建过程到底长什么样,看这里啊,呃这是我们一个类啊,就它类名叫什么,不管它有一个成员变量,number值等于八呃当我们new这个对象的时候。
new这个对象的时候需要你注意的是,new这个对象的过程,我想给大家讲清楚,就是我们构建java构建一个对象,呃,其实c加加里面构建一个对象,python里面go on里面各一个对象。
它的它的这个过程是一样的啊,那都有这个过程,就是构建一个对象的过程,大概长什么样呢,呃我知道很多东西可能没有学过c,没学过c加加呃,但其实这就是你跟计算机专业科班出身的,他的差距啊。
我希望给大家补补一点这种基础的知识呃,我们所谓的构建一个对象啊,比如说我们现在有一个类叫t,然后它里面有个成员变量叫number是吧,其实我们所谓的构建一个对象呢,就是首先我用这个对象的大小。
在内存里面申请一块空间,构建出一个空对象来,然后它里面不是有个x吗,哎我把这个x的值呢再付给他啊,比方说等于八就ok了,这个大概是一个过程,下面呢我们来跟踪一下这个过程啊,就是new一个对象的过程。
认真看认真看啊,我给大家呢为啥在看清楚这个过程,我给大家这块一定要跟上啊,就是我刚才讲完了111万,1万多的这样的面试题,我们现在下面要讲三四万的面试题啊,这个面试题呢肯定会稍微难一点点,注意看。
我们看这个啊object,我们看一最简单的就是object o等于new object,我们这是一个最简单的java程序了,我跑一下嗯,好我们跑完这个程序之后啊,大家都知道这个java程序的执行呢。
它最后会翻译成class文件,会翻译成为一次解码啊,就是它内部的执行过程到底什么样,我们可以通过观察它的字节码的执行,能知道它内部是怎么执行的,好注意看我们观察它的字节码。
will so bad code啊,去显示它的字节码是吧,找到方法这个了,这一栏找到main方法,找到它的code,好注意看这个就是整个程序的执行过程,这就是整个程序的运行过程。
就是这也就是我们另一个对象的过程,这个过程呢呃看上去跟天书似的,不过没有关系,我稍微给你稍作解释,你就理解了啊,这里面起作用的指令大概有这么几条,第一个是new申请一块空间。
第二个叫invoke special,调用构造方法,第三个叫a store,建立关联,duplicate,不用去管它,return,不用去管它啊。

我现在不跟你解释那么多乱七八糟的。
系列 6:P28:并发编程底层原理(二) - 马士兵学堂 - BV1RY4y1Q7DL
我让大家看清楚这个东西来画一个图啊,这个也是美团的其中的一道问题,就是构建一个对象的过程,对象的构建过程,好我们来看这个对象的构建过程,具体的class t m等于八,然后呢t小t等于6t啊。
我们怎么把这个t给构建出来,这个汇编码呢,我给大家写在这儿了,这是第一句话,叫new new是什么意思啊,兄弟们很简单,就是t有多大,就在内存里面给他分配多大的一块空间,o不给他放这儿好吧。
就把这个t的大小的空间给它放,大家都知道t里面有一个成员变量小m好注意,当我们把这个东西给它分配出来之后,我就现在问你没有执行后面的时候,这个小m的值等于几,等不等于8=8吗,等于几,这小m值等于几啊。
兄弟们来告诉我一下,末乘车等于零,没错啊,说的很对,就是最开始的那个默认值吗,你不给它赋值的时候,它默认值不就是为零吗,是不是这小m值等于等等于零,好注意执行到这句话的时候,这小m值等于零。
那什么时候这个m值才会变成八,是调用完这句话叫invoke,invoke是调用的意思,special叫特殊调用,调用了哪个方法呢,t initialize,也就是t的构造方法,只有调用完构造方法了。
你才这个值才能确保它变为八,ok好,第三句话叫什么,第三句话叫建立关联,a store,就是这个小t说明一个指针指向了这个对象,建立管理好看,这个动画看这个动画,这里有个小t。
我们现在要new这个对象了,首先分配一块空间,m值变等于几呢,注意m值等于零,接下来什么时候才会变成八呢,执行完这句话,invoke special这个值才会变成八,看懂了吧,那最后这句话是什么。
a store,什么小t和这个对象建立关联,好,我们讲到这儿,讲到这里,大概呢有呃,new一个对象三步构成,第一步分配空间默认值零,第二步调构造方法,附初始值八,第三步建立关联,记住了吧,再说一遍。
首先申请一块空间,复默认值零,接下来调构造方法,赋初始值八,然后建立关联,通过这个指针就可以访问这个对象了,这块听懂的给老师扣一,那这个this指针又是什么东西啊。
this指针就是这个对象里面有一个this,它指向的是谁啊,指向是他自己是吧,大概就这么个意思啊,好认认真真听我说,当你理解了这个过程之后,我们下面再来看看刚才的那个小程序,它的bug点在哪里。
回到刚才这个小程序,著名的类似指针的溢出问题,在这个呃溢出问题里面呢是这么来写的,t03 ,this escape number等于八,构造方法,这是它的构造方法,构造方法里面我启动了一个线程。
new thread,在这个线程里面有执行什么东西啊,打印this number,打印this number,我我我正常的期望值,应该它打印出来是个八,是不是我在我的主线程里面,直接new了一个对象啊。
就可以了,ok大家仔细读,就这么简单,小程序有没有bug,bug在哪里,有没有,上号说零对嗯,为什么有可能会是零啊,兄弟们,这不对呀,我这点number为什么可能会是零,我刚才讲过。
是不是构建一个对象的时候申请一块空间,首先把number值,这个时候值是几啊,兄弟们是零,假如说啊我还没有来得及调用完我的构造方法,注意我们说我们调用完构造方法之后,才能保证它变为八。
我还没有来得及构变这个构造方法给给他,给他那个那个调用完成,但是呢由于我执行了其中的一句话,这个线程已经启动了,线程已经启动了,整个方法构造方法还没结束,既然县城已经启动了。
我这个这个这个number值还没有变成,我期望的这个初始值,我这时候去访问的number,是不是非常有可能读到的是它的结果是零啊,就是读到了一个我们不想让别人看到的那个值,来这块听懂了,老师扣一。
来各位听懂了,老扣一了,就这块没问题吧,这就是说,当我们还没有把这个对象构建完成的时候,我这个this指针啊就已经被其他线程所用了,这个就是所谓的this溢出,好听,我说听我说来这块应该没问题啊。
比较简单啊,有同学说这就3万了吗,没没没没有没有啊,这个离3万还差一些,离三四万的面试还差一些,大家看这里啊,认真听啊,这个呢我们呃就是说this,等于说我对象构建了一半。
但是我这个this已经被被被被另外一个显著,拿去用了是吧,这个肯定是不对的,那么怎么改正这个这个这个东西啊,怎么改正啊,这玩意防不胜防不胜防啊,呃在这儿呢,如果你读过这本书的话。
其实你就应该知道这本书的名字呢叫effective,你知道吧,有同学读过这本书的,给老师扣一,我认识一下effective java有没有读过这本书,很有名,effective java出版了之后。
由于这本书造成的影响特别好,结果后来又出了一系列的effective,这个effective那个effective c加加啊,effective c,effective python有没有不知道啊。
反正出了一系列的,它里面讲的其实很多东西呢也比较简单,就是你编程序写程序的时候一些基本原则,其中有一条规则,其中有一条规则就是注意,不要在构造方法里启动线程,为什么,因为你在构造方法里面启动线程的时候。
有可能会读到初始化了一半的对象,叫半初始化对象,其中有一条原则,我再说一遍,叫不要在构造方法里启动线程,以前有同学不理解为什么,现在是不是能理解了,像这条这条习惯犯错误的人特别多。
尤其是写那些个游戏游戏服务器的,好多游戏服务器就new一个game,其实在在那个game的构造方法里,就直接把现场给启动了,不出错是不出错,一出错你永远也查不着原因是什么,好记住这条规则了吗。
ok下面我们来看一条面试三四万左右的难度的,和这个有关的问题,好吧,也就是美团的第二问,这位兄弟拿下美团这个七连问之后,大概嗯拿了应该是拿到了50万的年薪,呃美团大概。
这个当时问问题的时候就是围绕这句话,围绕这句话就看你的基础到底深不深,围绕这一句话,大概问了七个问题啊,第一个问题是对象的创建过程,这个我已经给大家讲过了,不重复了,第二个问题,这是第二问。
答出这个问题来,唉,基本年薪四五十万了,dcl要不要加volatile这个问题,这个东西我估计好同学,有的同学可能不知道什么是volatile,有同学可能不知道什么是d c l。
这个呢我需要给大家慢慢解释啊,呃同学们听我说这个d c l不懂得它的全称呢,叫double,double check lock,叫双重检查锁,听懂了吧,double check lock叫双重检查锁。
来这个概念,如果你没听过,你给老师一个反馈,你扣个二,我看有多少人没听过的啊,我需需不需要解释一下,嗯基本上每次都得都得解释啊,那个其实这个问题呢,其实要花费的时间还比较长。
大家伙认认真跟跟着我的思路走啊,这里呢需要打开我讲的另外一门课,这门课呢叫做设计模式,设计模式一共有23种,是提升你的设计实力的,一般来讲我们是刚开始那个入行的时候,是做落地啊,别人给我设计好了。
我去把它实现那个呃当我们的能力越强,年龄越来越大,就越应该变成有一定的设计能力,设计模式是最初级的设计能力啊,我再说一遍,设计模式呢是最初级的设计能力,ok,我们在这里的呢一共有23种设计模式。
组合搭配使用,这里呢我给大家讲最简单的一种,这种这种模式叫做单例模式,这个比较难的呢,像telemethod strategy这些observer proxy,这些都比较难,应该说proxy是最难的。
single呢就是我们现在要讲的这个单例是最简单的,单例的这种设计指的是什么东西呢,就是我们需要有一种机制保障某一个类,不管它类名叫什么,它有且只有一个对象,一一般都知道我们构建一个类的时候。
t我们想要它两个对象怎么办,我扭两下new t一个再new一个t又一个,但是我们能不能设计一种机制,在某种情况下呢,我们让这个类啊只有一个对象,不能有多个啊,这种情形呢其实用的比较多。
嗯我相信稍微有点经验的成员里边用,应该是用的都非常的多,他说某一些做配置管理用的类,只需要有一个就够了,举形象点的例子,就是说你作为一个man或者你作为一个woman。
那么你只需要有一个你的husband,或者只需要有一个wife,你不能让他弄好多个,你不能想new多少个多少个,这不行,那么怎么在机制上,怎么在语法上来构建出这个东西来呢。
啊最简单的实现方式大概长这样啊,就是我们有一个类,我们首先呢对这个类进行一个初始化,构建一个对象啊,instance,这就是它的一个有一个对象,构建完了之后,注意我们把这个它的构造方法。
major 01设成private,大家都知道我设成private,就只能我自己用,其他人就用不了了,ok那别人用不了了,别人想用我这个对象怎么办,我给他提供专门一个方法叫get instance。
ok当别人想用的时候,你就调我这个方法就可以了,这样啊在我们的主程序里面,无论你调用多少次,你get instance,你get instance,无论你调用多少次,最后呢我们得到的这两个对象。
其实都是这个对象,都是instance,这大家能看懂吗,就非常简单的实现单例的方式方法,那这块也能看懂的,老师扣一啊,比较简单,这个不难不应该看不懂啊,好你能跟上这件事之后呢,呃就是我们来进一步探讨。
实现单例模式的更好的一些方法,这是最简单的方法,也是最实用的方法,如果没有,如果有人要求你实现单例模式,没有特殊情况下,你就用这种简单安全容易理解,其实越往后越麻烦啊,当然麻烦有它麻烦的一点点好处。
好处就在于效率高,注意看,现在呢大家注意看,我们首先这个单例它是这么来实现,就是不管你有没有用到这个单例,get insense,有没有用到这个对象,我们在这里呢二话不说都把它new出来了。
客人想过一种情况没有,万一我们把它new出来了之后呢,没有人用它,没有人用,你是不是把这个对象弄出来,白白占了一块内存,这个东西吗,那这个事儿不太好是吧,那不太好怎么办好,吹毛求死的成员们就开始追求说。
那我能不能有一种方式啊,我要用到这个对象的时候,我再把它初始化啊,如果没有用它的时候,我就不把它初始化了,这样子可不可以,答案当然是可以,所以就诞生了这种写法,这种写法非常的简单好看,这里我在这里呢。
我构建了一个对象,但是注意看我没有让它初始化啊,我没有把它设成new什么,在什么地方把它弄出来呢,在这里谁要调用我的get instance的时候,也就是要使用我的时候,我判断一下,判断是不是冷空。
那如果等于空,说明没有人把它初始化,接下来我把它初始化,中间,这个sleep是为了在多线程的实验环境之下,让错误展现出来用的,没有什么其他的业务上的意义好,我首先判断判断这哥们如果等于空。
那么就把它扭出来,那如果不等空,我就直接return了,直接你直接你就拿去用了好了,这样的方式看上去比刚才的那种要好一些啊,是吧,咱们要好一些,就是我什么时候用它,什么时候new它什么时候不用呢。
什么时候就不new,那但是这个方式有致命的缺点,这个致命的缺点就在于并发的时候,就在于多线程的时候啊,看这里,现在呢我这里有100个线程,每一个线程都调用了它的get instance。
拿到这个对象之后,打印了他的hash code,就是把它的hash code打印出来,唯一码啊给打印出来,我们跑这看啊,不能说唯一码就是把它的哈哈希码打印出来啊,100个按理说应该是同一个对象。
但是我们往上拉你会发现这都好多类型,468结尾的,三二结尾的,264结尾的,434结尾的,看到了没有,你会发现实际当中我们用了好多个,这可不是单例,这些人好多例了是吧,多例为什么呀,这个能理解吗。

简单解释吧,好不好,避免有同学。

水平太差,同学们,你们想一下啊,现在我们假设有两个线程去访问这段代码,这是第一个线程,然后呢,第一个线程执行到这里,if instance等空啊,判断确实等空,第一个线程暂停还没有来得及往下执行的时候。
sleep了吗,sleep了没有来得及往下执行的时候,第二个线程来了,第二行程判断instance等空等空吗,没问题,还是等空,第二线程继续往下执行,然后new了个对象,注意这时候对象已经new出来了。
放在这放这儿了,第二线程结束好,第一个线程在这个时候继续往下运行,注意他已经判断完了,所以他又new了第二次,来这块能看懂的老师口音,有没有问题,ok所以这个以后我们就用了好多个对象。
当然第二个线程为什么没有sleep大哥,他sleep我只是让大家伙看到的,你这个你问出这个问题来,我只能说明你对于程序的理解到位啊,就是你如果理解程序在cpu里面,就线程的cpu没运行机制的话。
你就要知道中间是有可能随时暂停的,随时暂停啊,我放在这里让暂停时间长一些,只是为了让大家伙更容易看出这个毛病来,好了,我们就讲到这里,上锁,什么叫上锁呀,就是我们写synchronized就可以了。
synchronized的概念是什么东西啊,最简单的理解就是,一定要这个线程执行完整段代码,其他线程才能执行,能看懂吗,就同样一段代码一定是这个线程先执行,完完完成了。
这个线程必须结束了另外一个线程才能开始,所以这就保证了,无论如何,我一定要把它给构建出来了,另外一个行程一开始发现他已经不等空了,ok那就直接拿去用,这样的话只要有一个线程完成就可以了,这个叫上锁。
没有问题吧,就是枷锁对,但是当我们把锁加在整个方法上的时候,我们通常讲呢说这个锁的力度比较大,为什么呢,因为它你可以简单理解为叫锁住的是整个方法,就是一定是整个方法全部执行完,另外一个线程才能执行。
整个方法效率太低了,因为其中有一个有可能有一些单独的业务逻辑,比方说数据库里面读个数据,文件里面读一个数据,就是就是打着输出一些数据,就是打印和和读取,没有没做任何修改。
那你有没有必要把它放在锁定的代码里边儿呢,没有这个必要是吧,同学们,本来呢你咱们说上厕所是吧,上厕所应该是你上完我上,他上第四个人在上,本来就就这么就这么点事儿,结果你非得在前面加好多业务逻辑是吧。
先吃饭睡觉啊,吃饭睡觉是吧,然后上厕所就变成了你吃饭睡觉,上厕所完了才能轮到第二个人吃饭睡觉上厕所,你这个时候的效率就变低了,来各位能听懂吗,这应该没有问题啊。
这专业名词叫critical section,临界区就是临界区越小越好,o就是我一个线一个线程单独执行的,这个代码的量越少越好,效率越高越好,ok那大家理解这件事之后,我们怎么解决这个问题。
其实也非也就非常简单了,我们不要在整个方法上上锁,我们放过那些业务代码啊,放过某些那个不用上锁的业务代码,我们在这里上把锁就可以了,if instance等空,然后上锁,然后new对象,这样子就可以了。
好这个小程序能看懂的,给老师扣一,没问题哈,过不错挺好那好,那这个小程序能不能解决,我们多线程访问之下会出现多个对象的问题呢,能不能实验一下,跑一下你就知道了啊。
看这里又输出了一大堆的hash code,你会发现还是有好多个六四结尾的,三二结尾的,六八结尾的,434结尾的是吧,还剩好多个,这就奇怪了,明明我上了锁了,他最后的结果还是不是我想要的好,我告诉你。
这就是多线程编程的比较难的地方,断轴编程难就难在这里,很多时候县城之间开始互相影响的时候,如果你的逻辑稍微有一点点不对,你认为你上了锁,也有可能你得不到你想要的结果,我看这里,那为什么他会不对呢。

为什么会这样呢,我们看这段代码。

同学问我们仔细读这样的代码,虽然说它上锁了,但是我们依然通过刚才那个方法啊,第一个线程来了,判断等空,第一个线程暂停,第二线程来了,判断等空没问题,也是也是等于空啊,第二个线程呢上锁申请上锁锁的问题。
明天我给你详细讲锁的问题叫做原子性啊,那当你理解了这件事之后,上锁上锁成功了,没问题吧,然后呢new了一个对象出来好了,这时候第二第二个线程啊,new了个对象出来,new完了之后呢,第二个线程结束。
注意线程已经new出来了,对象已经new出来了,那第一个线程是暂停在这个位置了,那第一个线程继续申请上锁,能不能成功,可以,为什么第二个线程已经把锁释放了呀,第二线程已经结束了呀,上锁成功哎。
又new了一个对象,这样的我们就溜出来俩,这里就是在代码不对的地方,你会发现呢,为什么不对,其实主要就是指的是我判断完等空之后啊,我在申请上锁的这个过程之中,还没有上锁成功。
其他有的线程已经把它给弄出来了,所以我们要改正这一点呃,所里面在做非空调验,没错是说的非常对,这就是我们最终的代码,大家应该都能理解了啊,这次就对了,没错啊,这种写法就没问题了。
这就是我们啊最终的这个写法,这个写法就没问题了,就是我们首先检查它是不是冷空,如果等空上锁,上完锁再判断一遍,说我在上锁的过程中有没有被其他人改掉,再判断一遍,你是不是依然为空啊,如果依然为空。
把你丢出来啊,那么通过这个呢,我们再来把这个刚才逻辑的跑一遍,一个线程来了,判断等空没问题,暂停第二线程来了,判断等空没问题啊,第二个线程继续往下执行,上锁成功,依然为空,new了对象new出来了。
好第二线程结束好,第一个线程继续往下运行,上锁成功,又进行了一次判断,是不是等空,注意已经扭完了,不等空了,你就不会再用,第二下直接拿来用,o这个东西就是外面一层检查,里面一层检查,中间夹了一把锁。
所以叫双重检查锁,叫double check lock,好这个技巧在各个开源软件里面广泛运用,麻烦你好好掌握,多多的掌握,来这块能听懂的d c l的概念我就解释清楚了。
double check lock,能听懂的老师可以啊,但是关于double check lock,double check lock,听我说它里面的问题还挺多的,其中最常见的一个问题是这样的。
第一个问题是这样的啊,有两个最常见的问题,第一个问题是这样的,比较简单,就是我外面这层检查为什么需要,我明明是可以不需要的,反正我都要上锁检查,我先上锁,然后再检查不就搞定了吗。
我外面那层检查为什么需要分析一下啊,其实我是外面那层检查是不需要的,为什么为什么要外面要写成检查,从业务业务逻辑上讲,我只要上锁,然后检查它等空,反正这段代码只有一个线程,为什么要。
提升并发言说嗯说的很对啊,沙华说性能对,说的很对啊,减少加速次数,yeah that,说很多的啊,细粒度使用跟那个没关系啊,减少竞争线程,省省队员对,说的很对啊,就是我们来思考一下。
如果说外面这层检查没有没有就意味着什么呀,比如说现在有1万个线程过来,我这种智能代码,注意1万个线程要上1万次,所要上1万次,上锁解锁是很耗资源的过程,是效率不高的过程,能省则省,尽量的不要上那么多锁。
听听懂了吗,那如果有这层外层检查呢,如果有一个线程,如果有一个线程已经是把这个对象new出来了,我们只要做一次判断,就省了一次上锁的过程是吧,同学们就不用上锁了,避免了不必要的枷锁,效率大大提升。
你要愿意,你们可以试试啊,你执行个11次,你看看他要多长时间,如果你外面这层检查没有的话,执行11次,看了多长时间是吧,如果说外面可以有的话,你看多长时间,你会发现效率上的明显区别之后就不展开了。
来这块听懂了,给老师扣一,这是第一个问题,但是关键是第二个问题,关键是第二个问题,这第二个问题才是要命的,这第二个问题是说就是一段这么简单的代码,居然还有隐含的bug,ok那这个bug就是美团的第二问。

这就是美团第二问d cl要不要加volatile,什么意思,我们首先分析啊这里面的bug到底是什么,分析这个bug,分析bug之前,我们先复习一下一个对象的创建过程啊,分析一下。
因为这个第一问和第二问是呃,强烈关联在一起的,呃,我们说第一问呢,就是说这个对象的创建对象创建的话呢,首先是吧,然后申请一块空间里面那个值呢所默认值为零,调构造方法得到初始值为八建立关联。
所以它由三部构成,new invoke和a store,注意这三步啊。
系列 6:P29:并发编程底层原理(三) - 马士兵学堂 - BV1RY4y1Q7DL
这个问题现在呢我们依然是这段代码,依然是代码if,因此n次等空,然后上锁啊,我们现在假设有依然是两个线程啊,兄弟们,两个线程看怎么给他摆一下,就就这样吧,我们依然是两个线程好,现在是第一个线程过来。
第一个线程过来执行到这句话,if因斯坦等空没问题,上锁没问题,判断等于空没问题,开始new对象,注意new对象是由几部构成啊,这三步是吧,new invoke a store,好了好了,听我说。
呃我再重复一遍,一个线程顺利执行啊,第一个线程顺利执行,判断等空了,上完锁了,判断依然为空,开始new对象了,注意第一个第一个线程还没有完,还没有完事儿,那第一个线程大家注意,看你你你们你们呃记不记得。
就是说我们开始new这个对象了啊,开始new这个对象了,第一个线程开始new对象的时候呢,它有三部构成,第一个线程开始new这个对象,new这里当然就生出一块空间来,注意这时候的值是几是零,认真听。
当我们new了一半的时候,后面还有两部还没有完成,这两步呢是调构造方法和建立关联,这两步好,我们现在还没有来得及执行,后面两步的时候,后面两步呢换了个顺序,我们讲过这哥俩是可以换顺序的。
你说先建立关联和先调构造方法,还有呢先调构造方法和后建立关联,有什么区别吗,最终结果是不是都一样的呀,反正就是t指向了一个对象吗,再看一遍再看一遍,第一个线程过来开始创建对象,创建了一半的时候。
后面这哥俩换了个顺序,后面这哥俩这哥俩换了个顺序好,这哥俩一换顺序不得了,相当于这句话,这句话跑前面来了哦,这句话什么意思啊,这句话是建立关联啊,兄弟们,也就是说他先建立了关联。
还没有来得及调构造方法好,正好在这个时间节点上,第二个县城来了,大家还记不记得第二个线程的第一句话,是不是判断呀,if什么t是不是等于空,来你告诉我等于空吗,t等于空吗,他10000%不等于空。
他已经指向一个对象了,怎么可能等于空啊,那如果他已经不等于空了,哎我t就直接拿来用了是吧,instance直接拿来用了,我这个时候就用到了初始化了一半的对象,注意这个对象还没有构造完成。
好再看一遍再看一遍,我们第一个再看一遍啊,第一个线程,一个线程稍微把这个缩小一点,第一个线程判断冷空上锁判断依然为空,开始new对象,new对象的时候呢,new了一半,后面这两句话换了个顺序。
先建立了关联,先建立了关联,正好在这个时间节点上,另外一个线程来了,它已经不等空了,直接使用使用了初始化了一半的对象,来各位能听明白的,给老师扣一,这个一定要理解,就是你想进大厂。
这种问题是一定要理解的啊,尤其是,想进好一点的企业,好当你理解了这件事情之后,有的人会说老师这事儿不对呀,注意看我们这段代码是放在了锁里面呀,我们new对象是放在了锁里,看到了没有。
我们这个对象是不是放在了锁里面,放了一把锁里面,是不是我们放在synchronize里面,你放在这里的时候,我们讲过这个锁被锁定代码是什么样子的,一定是一个线程先执行完。
另外一个线程才能执行这段被锁定的代码对吧,那你告诉我说我这个对象new了一半,被另外一个县城给看见了,你这不扯淡吗,我怎么可能会看见被锁定的代码,执行的一个中间状态,这不可能啊,听清楚这个问题了吗。
来听清楚这个问题的同学给老师扣一,这个问题是面试官会继续问你啊,你为什么不可能啊,对不对,你这个的代码放在锁里面的,你溜了一半啊,被别人防到了中间状态,你这不扯呢吗,如果是说我们这段代码。
第一个线程执行的是这段代码,第二线程执行的也是这段代码,那第一个线程和第二个线程,它一定是序列化执行,它绝对不可能说并发执行,一定不可能说我这个里面会读到,你这个的中间状态。
一定是你这边执行完了我才能执行,但是关键的问题在于,比方说我们现在,我们现在有,两个线程,两个线程执行的是同一段代码,关键问题在于不是说你锁定的代码是什么样啊,你要知道的是注意看啊,这是我们第一个线程。
一个线程判断冷空上锁判断依然为空,开始扭扭了一半,我们还没有来得及释放锁的时候,按理说你是不可能能够访问到的,因为你不可能执行里面代码,但是需要你注意的是,外面的这层判断。
这层判断有没有放在被锁定的代码里面,没有注意,是这层判断里面访问到了,你被锁定代码的中间态是这里访问到了兄弟们,能听懂吗,这块能听明白的,老师口音,那么它就意味着我外面没有上锁的代码。
能不能访问到上锁代码的中间状态呢,产生的中间状态呢能不能呢,答案是当然能yes,必须可以,你要不可以的话,你就相当于任何一段代码都上锁了,这个呢我在vip课里面写实验,向大家证明了的好吧。
在这公开课上我们就不证明这一点了啊,你知道这个就可以好吧,ok,这样的话你就透彻了理解的美团的第二问,至少当然人家的问题是要不要加volatile,但是你至少理解了他问这个问题的本质是什么,好吧。
volatile的问题啊,好了,我前面讲过的所有的东西,大家能听懂的,a老师扣一,十点钟我我一会儿呢再给大家讲,为什么加上volatile就不会有问题啊,因为我给大家讲呃。
我已经给大家呢放在了我们枫叶云笔记上啊。

是我们自己做的一个笔记,自己做的一个笔记软件,待会儿呢也可以呃拿来使用,目前免费啊那个,大家呢需要这个这个笔记的,我今天讲的东西啊,我估计很多同学如果你没听过,你会觉得老师。
我觉得我以后写程序我都不敢写了是吧。

都不敢写了,没法写了啊,这两句话随便换顺序是吧,然后缓存行我们看不见的效率损失等等啊,以前有一只同学有这样的想法,这样的感想,但其实我要告诉你的是怎么说呢,就是一般来讲呢,就是你理解一件事情的时候。
当你理解不深的时候,你觉得我什么都懂嗯,我不跟你讲这些东西,你看这个代码看着都比较简单,我给你讲完了你会发现哦,代码原来背后有很多很多的故事,但是不要自欺欺人,你不要认为他真的很简单。
同时人家大厂也会问你它背后的故事,但当你理解了背后的故事,你突然间发现这代码真的不简单是吧,不过呢当你后,随着你后面理解越来越深的时候啊,你会发现山还是那座山,学知识大概就是这么一个过程。
最开始看山就是个破山,有什么可看的,后来发现山里面的故事很多,各种细节特别多啊,看山不是山,当你慢慢的掌握了之后,融会贯通之后,看山还是那座山,只不过更通透了,ok,这个内容呢是我讲的一门课。
就是并发这门课这门课里面的一小部分啊,呃整门课呢它的内容比较多,我们在我们做了一个完整的,关于掌心的整个的序列,这个呢是课程中的其中一部分,那么。

这个序列是一个很有用的序列,这个序列呢我会用最快速的方式,五分钟给大家快速介绍完,介绍完之后呢,我再给大家讲为什么加volatile之后,为什么加了volatile之后,它就能解决这一系列的问题。
听明白了吗,呃这一序列呢我用五分钟五分钟快速介绍完,大概我追求的是全而且深,是大概整个序列是我是,我是我设计课程的时候所追求的东西呃,全体现在什么地方呢,从零开始,从你刚入行开始,一直到你年薪过百万。
大概在我们整个的mca的2023版里头,就全包括了呃,整个的系列的深深体现在什么地方,就是面试所问到的一系列的底层原理,底层的源码都给大家讲到位,这是比较难的,不太容易,很多机构讲课的时候呢是挑容易的。
讲应用级别的啊,教你怎么搭配,怎么配置,配置完了之后怎么运行,怎么调bug,不难,找一份初级的,找一份薪水偏低的问题不大,呃马老师这里呢是追求一个全而且深,不仅全而且深,只有到了一定的深度。
你才能拿到更好的薪资,从咱们入学开始啊,入学的第一课教大家怎么去提问,因为一个优秀的提问可以得到一个快速的回答,到学前的面试指导,学习之前的面试,当我们的架构是开始启航,启航里面呢会讲了很多。
我们计算机系的一些课程,进大厂所必修的计算机课,编程的基础啊,这计算机课程里面呢主要是包括了操作系统,网络组成原理,io编译原理,数据结构和算法,如果你是计算机专业的,你看上去没什么新鲜。
但是非计算机专业的特别多,补补计算机一些专业的专业课,然后开始讲基础项目,web项目常用的工具和软件项目呃,系列的框架,java开发框架,一系列微服务的系列框架,微服务的项目,算法与数据结构的大常客。
工作的软实力好,这篇呢其实叫启航篇,可以认为是入行篇,就是如果是你是零基础,没有入行,靠这一篇入行问题不大,在入弯行之后是提升篇,提升篇呢我们讲java底层的深入知识性能的调优。
好今天我讲的这个呢是属于这部分的,叫java的并发编程,并发编程里面呢呃我们讲了三大特性,就是在这里挑了一些东西给大家讲出来,原子性,可见性,有序性呃,今天讲的有序性嗯,讲了可见性。
那个原子性呢咱们明天讲给大家听啊,拿出这样一小节来做公开课好吧,那么将框架的原理和源码读典型的就可以,我们的一个学生拿下阿里p7 ,就靠典型源码就拿下了中间件的综合运用,实战的项目开发。
然后大概呢有十个左右的项目,供大家写在简历上,那么架构设计的理论,这个到p7 的内容了啊,呃架构方案,架构案例,微服务的专题,架构设计的知道应该叫模式和心法服务保障,service mesh。
新一代的分布式微服务云原生软件测试啊,那么当你到这一步的时候呢,差不多是五六十万年薪,七八十万年薪左右啊,到这篇就完成了,再往后呢说明要掌握大数据云原生重中之重,这没什么可说的,就是云原生的落地状态啊。
这篇文档啊,我希望大家伙拿走拿走,好好读一下,听我说为什么建议大家好好读一下这篇,因为这一篇呢,差不多涵盖了你从零到年薪百万左右,所走过的一系列的路,就是说你到年薪百万应该掌握哪些知识。
都在这里面放着吧,找老师,以前有同学大概跟我们课跟了2年左右,才报了名,实际上你2年前要报的话,他就不至于有现在的这种困境了,他现在是遇到困难了,想起我们来了那个嗯,如果说愿意跟老师学,你就节省时间。
不走弯路,刘老师来帮助你,怎么样快速的找到一条通道,我们去掌心去入行,就算你不想跟老师学,非得要自学,从头到尾读完,你也知道先该学什么,后该学什么,学到什么程度,会拿什么样的薪水,作为架构师。
你可以再了解另外一种语言,go on,作为架构师去了解c k a c k s好,在这个基础之上,我们进入到技术管理的视野,大型团队的管理,产品的管理,生产事故的分享,技术的选型管理。
ok这个呢p7 到p8 之间,如果有人特别着急找工作,我已经被裁掉了,我想一个月之内,两星期之内搞定工作的就业突击,面试指导,服务陪跑,什么叫服务陪跑呀,解问答,应该的课程指导,应该的滚动直播。
我们每天都有,只要有新技术,我们就有直播,陪跑里面还有很重要的一项,就是在在他们就业突击的陪跑,你呢呃面试什么时候遇到问题了,你要说不知道该怎么答,录音录下来找老师,老师帮你解答。
老师对你进行一对一的学习模块的定制和规划,老师对你进行一对一的全程陪跑,会实现非常快速的就业,快速的涨薪啊,项目的系列升级,有的同学呢写项目,你简历上写的不多,没有怎么办,没有项目经验,没没事儿。
来这儿选选项目,写上去,好了这是我们整个的课程可能超了五分钟,我就介绍完了,别的不想多说课程取得的效果啊。

我们就拿突击举例子吧,就拿突击举例子啊。

这是我们面试突击课,年薪涨6万,就是搞定了突击。

其他什么都没动,突击了一下,时间呢其实比较短啊。

我们搞突击的时间一般都建议比较短,1年之中的金九银十啊,这是突击三个月,这个时间已经比较长了。

我们看这个比较短的案例就知道了,这是上海面试突击一个月,八个offer涨薪入职,刚刚的2023年的2月6号看到吧。

突击四个月,四个月就不能叫突击了,正常学习了,武汉突击半个月啊,十天突击搞定,突击半个月啊,这都是突击半个月的。

这个是突击了一个月,16000涨到23000还可以吧。

16000涨到23000,就意味着每年涨了8万多,ok这就是收益呃。

别的不吹牛逼,在马老师这里,你如果找到比我们更全的,找到比我们更深的马老师,这课我就送你了,你找不到啊。
系列 6:P3:GC - 马士兵学堂 - BV1RY4y1Q7DL
ok我们继续在这里面呢,我给交代给大家澄清几个,你们在文章里头经常可能会读到啊,但是特别容易混淆的概念,就是minor gc y d c这个指的是啥,指的是年轻代的回收,如果你在文章上读到的话。
mandc y dc指的都是年轻代回收,什么时候年轻再回收啊,分配不下了吗,分配不下了,你是不是得回收一点,取满了你不就得回收吗,所以这个东西年轻的空间耗尽是被触发嘛,很正常,老年代的回收指的是什么呢。
一般的称之为major dc major,当然另外一个更加广泛的称呼叫做f g c,f的意思是full fdc,全回收,全回收其实指的是年轻代也回收,老年代也回收。
但有的时候在特定语境里指的是老年代回收,无所谓,能理解就行,概念这东西是人类发明出来用来沟通用的,原来的人类在这个在远古的人类,在广阔的无垠的大草原上,看到这么一个东西的时候,他不知道是个啥。
热烘烘冒着热气,后来他们肯定是互相打招呼啊,你过来看看这个模样的这个东西,后来终于有人类发明了一个概念,这个概念叫,说这个东西是个,它是不能吃的,ok互相之间发明出来沟通用的,你理解的东西是什么就行了。
说你非得叫它叫或者叫boopop,或者叫叫叫叫叫粑粑是吧,都都都可以,千万不要在这里咬文嚼字,凡是在这咬文嚼字的,你这个学学学习的方法,都是属于那种孔乙己式的学习,没意思啊,会把你学的特别累。
而且呢还没啥用好了,灵活一点啊,灵活一点,就跟刚才那p 10似的是吧,你承认你就承认他牛不就完了吗,你每年四五百万的年薪拿到了它不香吗,看这里啊,老年代无法继续分配空间。
是触发了新新生代老年代同时进行回收,就老年代满了就触发f g c了,这不很正常吗,刚才不是有同学问吗,你像这样的问题,为什么还要问老师,那我搞不懂什么时候我要清理啊,哎当然就是空间占满了,我要清理。
当然这是一个形象的概念,其实呢它有一些特定的垃圾回收器,每种是不一样的,有的垃圾回收器是占到一定比例的时候,就开始回收了,有的垃圾回收器呢,是需要确实要占满的时候才来才进行回收,而总而言之。
空间不够使了,我预测到空间不够用了,我马上要开始清理了,这个时候开始触发,所以下次你们再聊,这种概念的时候一定给我记着,你们第一次聊的时候,一定得跟面试官聊,我用的是哪种垃圾回收器,各种垃圾回收器。
他用的东西,用的算法以及各种触发的时机都是不一样的,你要这么问的话,就显得你特别业余,o我们再稍微回顾一下哈,我把一些细节先忽略掉,做一个小小的动画总结嗯,这个小动画呢大家看完脑子里存印象,一般来说。
动画的对你脑子里的刺激会要比图片要好,图片对你脑子刺激要比文字要好啊,所以我尽量的选择刺激你更加强烈的地方,ok,当然做动画其实挺难的,我这动画做了俩小时啊,基本上三分钟就给你演示完了,好好听。
当我们new出一个对象来的时候,首先他会在站上进行分配,你记着就行了,有这么一回事,如果你要非要细究这个概念的话,你学过c语言里面有一个struct结构体,你又能理解。
为什么java里面还可以在站上进行分配,在站上进行分配有什么好处啊,如果没学过,你就单独扔一边,不管它,总而言之,java某些特定的小的对象是可以分配在栈上的,在站上有一个什么好处,大家还记得吗。
这站是个什么东西啊,站里面装的战争,每个方法对应一个战争,对不对,哎这个方法里面用到的这些个对象,如果方法结束了,整个战争,背往下一盘,就是就是暂停指针往下一挪啊,这意思,里边这个内存就回收了。
就这么简单,所以如果你要是把对象,把这个对象分配在栈上的话,分配在这里的话,什么时候对象不用了,分弹出去了,给它效率非常高,它不需要什么呀,只需要g,不需要gc的介入,不需要清洁工的介入。
不需要你爸爸妈妈来帮你清理这些小线段,这是在栈上分配,如果能在栈上分配下,分配在栈上站,只要往外一弹,整个程序就结束,这个对象也就结束了,当然如果站上分配不下怎么办,分配不下,看这个对象够不够大。
多大是大,可以自己设置好,如果对象个儿特别大,这个时候直接扔到老年代,而不会扔到年轻代,ok老年代什么时候他才会被回收啊,只有经过fdc之后,那不大不小,也不能在站上进行分配,那这时候分配在哪呢。
这时候分配的一个空间叫t r a b,叫three local cation buffer,全称是这样的,thread,这不写了啊,这很简单,线程吗,local线程本地allocation。
lover好,allocation的意思就是分配的意思啊,allocation我就给你写错了,叫县城本地缓冲区,有同学说这是个什么东东,别着急,你听我解释,如果你在这里能分配一下,或者在这里分配不下。
其实最终都会分配到一边去,所以这个东西呢,呃你知道有这么一个东西存在就可以了,除非你调音调的特别精细的时候才需要调,这个一般用不着啊,一般用不着,t r a b是个什么东西呢,叫做县城本地缓冲区。
什么叫县城本地缓冲区,是这么一回事,当我们往依恋区里头分配对象的时候,我们有好多好多线程,每一个县城都要往里头分配,那就会有县城竞争,我们都要抢抢这个空间,第五个小格,我的我的我的我的。
那么到底是谁的呢,所以我的jvm要管理这些线程的竞争,因此我要浪费好多资源,管理这些线程到底竞争所同步吗,那那好,所以在gbm里面做了这样一个优化,就是每一个线程启动的时候,给每一个线程。
在伊甸区里面分配一个小小的空间,是这个线程专属的,我觉得这个是这个写成专属的,这是线程一专属,这写成二专属,当分配对象的时候,线程一的对象,优先分配在自己单独的这块空间里,不要去抢公共空间。
这样就不用进行线程同步了,效率就变高了,也不知道我说清楚没有听明白,同学给老师扣一,这个叫t r a b,对,象不是分配在堆吗,对你在去学幼儿园的时候,对象就是分配在堆的,你现在讲大学对象。
就是可以在分配在各个位置的,t a b跟sr local有什么关系,没有半毛钱关系,就是不需要加速,你说对了,超tvb是在伊甸园里的e目前是难怪要用线程池,我感觉你的脑洞有点大。
这跟线程池有半毛钱关系吗,好我们继续当一个对象进入一点去之后,经过一次垃圾回收,如果被清掉了就结束了,如果没被清掉,进入到survivor区,survivor区域又经过一次垃圾回收,如果清调结束。
如果没被清掉,看他年龄够不够,年龄够了,进入老年代,年龄不够,进入另外一个survivor,如此循环往复再看一下这动画,当我们new出一个对象来的时候,首先尝试在站上分配,不符合战上分配条件。
嗯符合就在这儿分配不符合,看它个够不够大够大,近老年代不够大,进t r a b t r a b装不下了,进伊甸区,不管是不是最后都是在一定区里,一区你回收一次进survivor 1 slaver。
一又回收一次,看年龄够不够,年龄够了,old区年龄不够,survive 2循环往复k来能get到这个动画的,李老师可以把图装一下吧,老师什么是线上分配条件,你这个太细了啊,s一和s2 互换位置。
他为什么要互换位置,互换个名字不行吗,大哥刚开始这个是空的,它叫做to,下一次那个是空的,那个叫土不就完了吗,每个线程都有一个特t r a b吗,4d,请同学们有点问题,我暂停一小小会儿给大家回答。
大家错了,这个这个这个常见的问题啊,t r a b再讲一下,不能再讲了,l a b再讲一下,讲啥呀,提问吗,经过tvb有什么意义,tvb县城本地的不需要加锁了,好多线程。
如果没有t r a b好多线程往e点去分配,是不是大家得竞争啊,锁竞争多线程嘛,你要多线程技术没有,那那可没法跟你讲了,就那大家不抢吗,谁抢到算谁的,我是不是g y m得花精力,换资源去照顾这些线程诶。
是不是该你了,是不是该你了等等,线程同步,但是如果每个人都有自己私有的一块空间,我要拿到一个对象之后,优先往自己私有空间里装,我需要跟其他人做同步吗,需要跟其他人抢吗,不用不用,我是不是效率就提高了。
是的,可以关闭t r b e s,还可以用cs,你这不废话吗,近战条件是什么,近战条件叫做可以进行标量替换,以及不能存在本地逃逸,也就是说要进行逃逸分析和标量替换的分析啊,怎么定义大对乡村的max。
什么什么什么什么size threats hold,你搜搜一下,一搜就搜到了一电车,什么情况,直接到end,年轻代啊,年轻得回收,清完把它把它都清了,不就end了吗,t l v d和县城站有什么关系。
没有半毛钱关系,stop the world good,什么时候t l b放不下,装满了就放不下了。
系列 6:P30:并发编程底层原理(四) - 马士兵学堂 - BV1RY4y1Q7DL
呃对于课程有疑问,要下面我们回到技术继续来聊,怎么样才能解决乱序的问题,ok下面这段还比较难啊,你认真听,来准备好的同学给老师扣一,下面这段还比较难,认真听,好听我说听我说。
嗯我在这里给大家简单画图讲解,仔细听,就是同学们你们认真想啊,说我们现在的多线程的情况下,引起问题的麻烦的原因,其实原因就在于这两条指令呢,你不能够控制它换顺序,或者说你不允许他换顺序,能不能做到呃。
我们先讲最底层的,去限制两条语句换顺序的这个逻辑好吧,我们先讲最底层逻辑,它跟语言无关,在这呢你忘了各种语言,不管你是学够学java,学学c,学c加底层的呢,它都是一样的,掌握底层就掌握一切啊。
仔细听就是同学们,你们想一下,这里来了两条语句,这是第一条,这是第二条,我如果想阻止这哥俩换顺序,你该怎么办,从底层上讲,就好像说我们排队去买咖啡,我我我必须阻止,说这哥们儿呢插队插到前面去。
怎么阻止他,就是我把这两条语句之间,或者两个人之间给它射度强,能听懂吗,就是我把这个这个已经执行完了,我这堵墙我才放开,我再把这墙拆了,再让再让第二个人过来,这样他就永远换不了顺序。
所以呢所以呢实际上是在这里啊,兄弟们,这两条语句如果想从底层来讲,不想让他换顺序怎么办呢,我们在它中间插一个插一条特殊语句,他也是一种语句,这种特殊语句呢起到一个一个强的作用。
好这种语句呢一般我们称之为叫屏障,叫内存屏障来这块能听明白的,给老师扣个一,建个墙,对,不是阻塞沙皇加锁,我勒个去,你为了换两条语句,你可以加个锁,你累不累啊,而且加锁。
你能你能限制得了他这个语句的顺序吗,你每条语句都加个锁啊,你想啥呢,那那那程序你还怎么执行,是屏障指令,一般呢它是称为叫屏障指令,这个屏障指令呢比较特殊啊,这第一句话,这是第二句话。
我不想让这样换顺序怎么办,加一个屏障指令,屏障的一般我称之为叫barrier是吧,嗯或者叫fans fans呃,篱笆barrier啊,篱笆大概都都就是这个意思,屏障好,这个屏障指令呢请大家记住不同的。
不同的cpu有自己不同的屏障指令,我们是没有办法给他统一的,ok这个屏障指令呢,呃你在如果你涉及到了不同的这种这种这种cpu,不同的硬件,你会做,你会看到它屏障指令不一样。
你比方说像那个英特尔的air fans,load fans读屏障,这个m fans写屏障是吧,modify修改,还有s fans,对不起,这个应该是全屏障啊,fence是save,是修改的意思等等。
反正我忘了啊,就具体的指令也就意味着当我们cpu执行的时候,执行的,只要看到这种凭证指令的时候,他就前后两句话就不让他换水去了,来各位能听明白的,给老师扣一,这是底层的逻辑,这是底层的逻辑啊。
啊这会大概能听懂,那老头还能还能跟上吗,这个难度还能往上加一点点吗,能跟上,我就在难度里稍微加一点点,每次讲到后半截,我我争取给大家解释清楚,因为我觉得我讲的东西啊,它再难,老师还是能给你解释清楚的啊。
呃只要你认真跟上就可以,除非你基础贼差贼差,那就有点够呛啊,仔细听,那么这是底层,我们说不同的cpu有不同的解决方案啊,这是底层,ok好,下面我们来再往上挪一层,这层是这一层是哪的,这一层是jvm。
就是虚拟机,这层java虚拟机,那在java虚拟机这层里面呢,虚拟机啊就是一台虚拟的机器,一台虚拟的机器也有它自己的指令,这个我相信大家都能知道你你实际当中的机器,实际当中cpu有自己的指令。
那么虚拟的机器呢也有它自己的质量,当然当我们得到这种指令的时候,需要翻译成为下面不同的指令,才能具体在具体的硬件上运行,那虚拟机里面的这种屏障指令是什么呢,注意啊,这个并没有一个cpu支持啊。
听我说目前没有cpu支持,因为它是虚拟的,是软软的模拟出来的啊,那jvm要求的虚拟的,你你谁要是给我写一个jvm软件,你必须给我实现这种指令要求的,虚拟的指令长什么样呢,在jm虚拟指令里面呢。
大概有四条指令好,后面的呢你就能听多少是多少了,因为呢这个大厂也就连大厂都很少考的东西了,目前为止只有一个同学去面那个阿里国际,一共面了七次面试,阿里面七面面进去才考到了这种深度啊。
所以呢你就大体理解理解到什么程度算什么,算什么程度就行了,好吧,我只是讲给你听啊,能听懂就听,听不懂就算了,就是jvm级别的屏障啊,大概是长这样,跟happen before没有半毛钱关系啊。
目前还没有关系,jvm的屏障大概长这样,注意看,它有四条屏障指令构成,这四条呢叫load load store,store,load store和store,load叫load load,load。
store,store,load和store store,其实就这么简单啊,几个的排列组合而已,有同学说了,老师我想这太难了,一分钟给你解释清楚啊,老师只用一分钟时间解释这四条指令非常简单,对一个数据。
如果你前面有一条读指令,下面这条也是读指令,你不想让这俩换顺序,中间加一个load load,这哥俩就换不了了,听听明白了吗,好剩下的还需要我解释吗,想一下下面这些还需不需要我解释,来听懂的给老师扣一。
啊不用一分钟是吧,搞定那store store指的什么,解释一下吧,你看看大家伙,那个那个是不是学到了上面一条store,下面一条是store,如果中间不想让这俩换顺序怎么办,store store。
ok就这么简单,那好了,这是我们的jvm级别,那我们再上升一层,刚才呢我们解释了最底层,解释了jvm这一层,我们再上升一层,上升到我们源码这层,java这层。
因为java编译完了class交给jvm执行,jvm再翻译成为本地的是吧,大概是这么一个执行过程吗,好,我们来了解java这一层是怎么样,不让语句换顺序的,好java层不让语句换顺序是怎么做到的呢。
很简单,加一个volatile就可以了,ok,从语句上来说,大概就长这样啊,你找找一下语句吧,看这里,如果你你不想让他换顺序,刚才不是由于换顺序,构建过程之中的顺序调换引起的,是不是。
因为他这个换顺序引起的各种各样的麻烦事,你不想让他换怎么办,好对这个变量做一个修饰符,写volatile搞定,好了,这就是解决方案,可是有同学啊,如果你进行认真思考的话,如果你认认真真思考这件事啊。
还还在吗,兄弟们还还在跟着我的思路走吗,还在的给老师扣一啊,越到晚上的时候,你越得越得给我点反馈啊,可是你认真思考这件事的时候,这件事很吊诡,吊轨的点在于什么地方啊,吊轨的点是instance是个变量。
你只是给它加了个形容词,你可以这么加了个修饰符嘛,就加了个形容词,这个变量是一个volatile的,就是就是这么一件事儿,居然阻止了对它构建的时候,以及其他方方面面的时候的这种顺序,这不是很吊诡吗。
而且这个volt还可以修饰简单变量,你说修饰简单变量他都没有构建这个过程,你怎么阻止对这个变量的访问,前后不能换顺序,这事儿是怎么完成的是吧,同学们,所以这里头中间还差了一个环节。
就是当我在java源码里面写了volatile,它翻译成为jvm的时候,jvm的class代码的时候是翻译成为什么样子的,好各位同学认真听他翻译过来,大概长这样,这块呢能听懂就听,听不懂就算了。
没有什么关系啊,关系并不大,应该说好吧,使劲儿听就行了,大概是长这样的,就是当我们用volatile去修饰一个变量的时候,那么对于这个变量的任何的写操作。
前面都会加一个store store store store意味着什么,写这个变量先写完,我在写,后面对于这个变量再加一个store load,我写完别人才能读,这就是对于修饰变量的时候。
对于修只要是任何只要是volatile修饰的变量,那么对于这个变量的任何的写操作,前面有屏障,后面有屏障,别人写完我再写,我写完别人再读,你不能随随便便的给我改顺序,对于这个变量的任何的读操作。
我读完别人才能读,我读完别人才能写,好了,这个图呢能记住就记住,记不住就算了啊,基本没人考,好吧,就到这ok,呃这样的话呢,我就从那个底层,java层加volatile,volatile翻译做的变量。
在我们jvm这层会给我们自动的加屏障,而这些屏障只是一个虚拟的屏障,这些屏障最终会被我们翻译成为底层的屏障啊,这些屏障呢才是真正的具体的实现好了,这块能跟上,能听懂的,给老师扣一。
其实嗯还有一些更深入的博克就不这么深了啊,没有这个必要了,好吧好,讲到这里的时候呢,呃你翻回头来再来看美团的题。

你就会觉得比较透彻啊,清晰明了很透彻,一夜就具备了去拿一个50万年薪的想法了,至少,好同学们嗯,我大概的内容呢,我基础上的内容,今天我就讲到这儿,笔记里面我做了详细的记录,关于笔记的内容呢。
同时呢我也对我们课程的这种达到的效果,我相信课程的深度你应该能理解了啊,基本上都是这种深度,这也是就这种深度,再加上我们老师呢是为学员们一对一的定制,学习路线,因为有的人掌握了a没掌握b掌握b了。
没掌握c,有的人快速要进行业,有的人呢是要快速的涨薪,有的人呢是快速要回到行业,其实每个人的追求的内容是不一样的,所以呢我们做到内容全非常全的,同时200多门课啊。
我们整个mc呢是提供了目前是200多门课。

mc目前提供200多门课。

从第一节开始,我们大概提供了是200 229吗,我记得是啊,大大小小229门课程,涵盖了绝大多数程序员所需要的所有所有内容,然后呢在我们老师的指导和定制之下,可以进行一个优先先学哪部分,其次再选哪部分。
然后再选哪部分,我们进入到行业或者转完新。

接下来进行系统化学习这么一个过程,来各位听懂的给老师扣个一啊,c加加做内存屏障,c加做内存屏障,使用汇编语言做的啊,这如果要是感兴趣的话呢,明天我给大家可以打开来看看,其实我们也是能读到的嗯。
这个达到的效果呢,大概在我们这个公开性的网页里,这里的网页里,你看到的所有的内容都是不给你p,非常真实的信息,除了隐藏了一些隐私数据之外,其他的全都是非常真实的数据,我目前我有学员呢。
这里头给大家展示出来的1500位学员,这里是1500位学员的数据,兄弟们,这些数据呢到目前为止三个月左右,基本就达到效果,达到效果呢当然跟自己的追求不一样。

它达到效果就不一样,有的是拿到offer 3个月,有的是涨薪水8000,涨15000。

有的是呃原来的年薪33万,然后呢到达了年薪40万,而且呢这个还是在咱们近期不好找工作的前,提之下啊,嗯最终也是按年薪40万算13个啊。

名义上那么多,实际也是惨淡断嗯,涨了20%吧,呃有的是大龄的乘以二,这个呢也是我们的一个特色,好多大龄都搞不定的,咱们这儿搞定了一大堆。

总之呢就是那会儿我我我创建这家公司,做这些课,其实呢一个核心的想法是,让程序员的生活过得更美好一点,没有入行的能方便他入行,入行的多赚点钱,年龄大的我教他怎么样去进行养老,怎么样去进行这种更长远的。
能做到退休的这样的规划啊。

方方面面吧,这是ood的嗯,找几个薪水略高一些的,这是秋招,多数达成最终入职压力了啊,年年报是47万,算签字费,这是很嗯非常就是最近的数据啊,短期的是吧,这是咱们的一个大龄的女同学。
这个大连的女同学是从50万涨到了75万,这事呢给他内部建议的一个过程,呃其实我就这么跟你讲,为什么建议大家伙要报名马老师的课,如果说这个课程只给你提供了一些硬件的东西,教你a b c d e啊。
教你一些应用知识,那我觉得这个课程的有有有价值,但价值没有那么高,我觉得我们还有很重要的一个价值,是帮大家梳理好整个职业生涯,就是有的时候你遇见难题之后,你可能自己不知道,不知道该怎么解决的。
你过来找咱们比较有见的老师来聊一聊,你就会发现李老师过去从山顶上下来的经验,一两句话,可能能让你就像这种的年薪涨了25万,这是一个什么概念。

每年涨25万,在以后的20年里,每年25万凭空多出来500万,把牛逼两字给我打出来好吗,这种案例不在少数啊,不是少数好吧,这个嗯,我们呃近期2月份的报名活动。

也就很快截止,就是今天是26号了嗯,今天是26号,对26号,27号,28号我们就截止了,我们在整个活动期间,2月22号到28号结束,我们进行了学费的补贴,然后报名的同学降了价,作为完整的一门课。
马老师这里提供了一系列的老师来为大家服务,这个应该也是大手笔了,就是有很多很多的机构讲一门课,其实就那么几位啊,两三位老师已经讲完了,但是我认为呢一是我们讲得非常的全,在所有机构里是最全的。
大概是排名第二的机构的课程内容的十倍十倍,兄弟们,这不是闹着玩儿的,我们价格比其他人最贵的,也就比他高个百分之几十而已,但是我们内容是它的十倍,为什么我们价格要稍微高一点。
因为我提供的老师数量比他的要低要多得多,我们现在有多位老师来陪跑,大家,多位老师帮助你进行面试突击的提升,多位老师全程指导你项目的实战,如果你需要录音分析的找咱们老师,如果你需要内推的,找他们老师。
这就是我们提供给你的服务,这个活动呢后面可能会打折扣,可能会随机分配,ok可能会你有有限的进行自主的学习啊,因为我们扶不过来了,要啊每个月的人数,我们现在呢逐渐开始设定限了,抓住咱们22号和28号的。
整个的活动期间好吧,有同学说老师哎呀,太卷了,卷的我搞不定啊,其实卷这个词我觉得是另外说,说白了另外一个那个层面就是努力嘛,卷呢其实就是有一个人努力了,其他人没有跟上,人家搞定了,所以其他人又想努力啊。
但是人家更努力啊,大家一块努力的这个过程,卷十十都有随时存在呃,不仅中国目前很卷,国外现在也开始面慢慢变卷,美国的大厂也开始玩命裁员了,这个大家可能了解过啊,所以呢现在的机会在于你是不是掌握的更深入。
是不是掌握的更细致啊,你的简历呢是不是更牛,你的技巧是不是到位,你是不是有一个优秀的导师指导的,你听懂了吗,到9月好找工作吗,你这个是预测啊,这个不好说,但我觉得呢与其你担心大的形势,是不是好找工作。
我觉得你不如把你的精力用在提升自己上,就是我们俗称的一句话,你呢不要用为那种改变不了的东西而烦恼啊,我就问你一句话,你说这个说外面的大形势不好,你能你能改变吗,你肯定改变不了,对不对。
那你改变不了的情况下,你就不如改变你能改变的东西,就是你自己的水平,机会就算少,是不是卷的更好的同学,掌握更深入的同学拿到的概率就越高,所以提升你的概率就可以了啊,不淡定,小青春怎么报名。
到官网上找我们咨询老师报名,或者在这里扫码报名,报名的话终生能有老师教学,你这个不能聊终生啊,现在就是各种广告上都不能聊终生呃,我这么跟你说,潇洒天下呃,做线上培训的,有一些机构倒闭了。
基本上都是玩终生的,你可以去了解了解玩终生的,我不可能说,因为你付出了这点成本,我覆盖你终生的成本不可能的,如果谁这么玩,最终的可能就是卷钱跑路捞这么一结果,所以没有终生,但是呢马老师这里呢。
就是说你你呃交完学费之后,如果后续有更新,比如说我们每年都在进行直播吗,直播的内容在不断更新,更新的内容,我们是这么定的,188亿年,正常的我们是收1808亿年,因为我们更新内容非常多啊。
我们更新一个内容,很可能是排名第二的机构的,所有的内容,知道吧,就就就更新的内容,就是这样,就一个增量就可能干过他所有呃,所以你申请这个188亿年,这句话的意思就是说,当你报完名之后。
1年之内你是收到了这个,如果你第2年继续想听我们更新的课,交一个流量费,因为我们从服务器上传上去,到下载到你手机里,这个流量很大,目前整个网站的流量,每天的数据流量大概在五个t左右,五个t。
这是对我们来说是一个巨大的费用啊,兄弟们,所以呢我们就收一个流量费就可以了1年,所以你不要跟我聊终身好吧,当然说你说那个嗯在后面,你比方说你你学完1年之后,你还有什么大的问题需要问老师的,这个没有问题。
你放心啊,只要咱们老师在你有什么问题的话,你都随时可以问,可以等有钱付款吗,先学这个不行,想先学后付款的,到马士兵教育的线下线下我们接受,因为我们见到你真人,确定你的信用能力没有问题啊,你确定你呢。
我们培养完你之后,你可以赚到钱,我们能接受你先学后付款,但是线上不行,要给情人说最近简历投了没反馈,是想饱和了吗,你怎么不在你自己身上找问题呢,为什么你不是说你你你简历不行吗,大哥要给情人。
你要是愿意的话,把简历发给他们一位小姐姐,你打开来,我给你看看简历,简历没反馈,很简单,是你的简历问题啊,跟人家市场关系不大,我也是没有反馈,刚工作1年一样的问题,永久这个说法大部分是骗人的。
对你认识到这一点的时候,你就离韭菜远了一步,4月份换工作,这个过程来的几步呃。

一个月时间我给你看几个一个月的案例,你就知道他来不及来不及了,走突击啊,我需要你走突击。

知道吧,这是突击一个月的效果,我不知道你现在的薪水是什么水平。

我给你看几个一个月的案例好不好。

你看这有88个和一个月有关系的,这是这是报名血迹的啊。

一个月涨薪涨了三三千五,不多啊不多,这是一个月涨了4000,虽然不多,1年5万不是白来的吗,白来的啊。

被裁了一个月,搞定两个offer了,就回到行业的,短期一个月在成都这边涨了4000,24岁的大专生,好吧。

呃这是突击一个月,然后45万年薪,一个月找到工作的,1113的,合肥的涨了3000的,整体涨了32%,1230的一个月,找几个一个月吧。

随便找一个啊,这是一个月涨了8000,跳槽上海。

涨薪是一定的,就是你用一个月的突击,注意是突击啊,兄弟们,突击的学习方法和我们刚才讲的,从头到尾的系列化的学习方法是不一样的,这个由老师来帮你把控就行了。

让我们来看看,老师我之前是制造业的,今年34了,学过1年半python呃,欢乐赛道,年初入职o d云数据库开发,起薪比低,我不满足现状,通过学习掌心有什么建议,职业规划不太清晰,首先首先选择哦。
你34岁了,34岁,你34岁相对麻烦一点点,你认真听啊,550q,如果你年轻的话呢,其实就是从头开始规划,换比较好的赛道就行了,但是呢你刚刚换了赛道,做o d云数据库开发呃。
你这个的话呢怎么去找这个薪水,你认真听这个比较难,就是你首先要了解这行里头薪水比较高的人,长什么样,听我说,这行里头呢大概是入行的时候,基本上有两种入行的方式,如果从后只单纯的后端来讲。
第一种是用java后端入行,第二种是用大数据入上big data这两个方向,然后呢沿着各自的路线发展,初级程序员,中级程序员,高级程序员,然后拿到一定的薪水之后,突然间都会遇到天花板。
想突破这个天花板的时候,二者合二为一,成为一名优秀的技术架构师架构,这就是咱们mc课在这里,那么成为一名优秀的架构师之后,你的薪资啊就可以大幅大幅度突破,听懂了吧,然后这是技术序列。
然后这个技术序列如果说做不太好,不是不能说做不太好吧,就是技术序列,还有一个选择就是管理序列,技术管理,技术管理,技术管理一般要求的是整个的呃技术的宽度,这宽度意味着什么呀,宽度意味着。
因为你要将来是带领完整团队的宽度,意味着你要对完整产品的,整个技术链条上的东西都要大致明白,包括前端,后端大数据,云原生ui设计测试运维产品,大致明白这个东西呢,我们用一个完整的项目教了大家。
叫咱们的马士兵严选,这个项目呢是我们大概花20 20多个人开发,来研发完成的,在这个里面涉及到了不同的团队怎么配合呢,在这里面讲的很清楚啊,如果你想了解这一块的话呢,到这个项目里面去了解,听明白了吧。
那像你的话呢,其实我觉得啊你可以你现在是走云的呃,云的话呢。

我不知道你现在所谓的云数据开发,指的是个什么,是只是写sql语句吗,如果你只是写sql语句,那就啥也别说了,那肯定薪水不会太高,天花板也比较低,很容易到天花板呃,如果是云的话呢,你你你你呢。
你打开咱们网站之后啊,你仔细去看。

仔细去看这部分就是容器云。

云原生架构师这部分,这里就是容器云这款,其实容器云这块呢呃所涉及到的东西还挺多的,就是虚拟化和云计算这块啊,以k8 s和docker为核心,然后进行一系列的开发和配置,当然也包括了运维开发的一体化。
云计算的技术啊,呃怎么说呢,你呢如果以sl为核心,还有一个方向,还有一个方向云的话呢。

可能有点偏韵味,这个不一定你想要的,还有一个方向是这里就是大数据。

就是你以云数据库为核心,大量的数据的处理过程啊,这块的话也能提升你的,这就是你的你的薪薪水的涨幅。

听懂了吧,大概这两个方向啊,你可以你可以跟可以选。

不知道说清楚没有,15k到20,咱们学突击也可以达到吗,可以,基本上问题不大啊,刚工作1年,12k本科二本三小时突击能到多少,二本本科1年不要跳槽,我告诉你后面怎么进大厂呃,突击的话呢。
你就别突击一下了,你就别突击一下,因为空白人生你还在吗,空白人生在不在,空白人生在的,给我扣个一,你还在听吗,给我扣个一,因为我想多跟你说几句,空白人生,你还在不在,你不在了,我就不想说了。
说起来就比较远了,不在了是吧,不在了不说了啊,没有意义了嗯,185 32岁,java后端13k入职,后期怎么规划,那个185你还在吗,给我扣个一出来,人还在不在,1853是吧,好认真听啊。
185我不知道,你现你现在你现在给我再敲几个问题啊,就是第一个呢你现在是在哪个城市,在哪个城市啊,给我敲出来好吧,第二个呢你告诉我你的学历是一本二本大专,985211什么什么的,你告诉我一下好吧。
把这两个告诉我一下嗯,185是在乌鲁木齐二本,其实你乌鲁木齐的话,据我所知薪水还是偏低啊,你32 32岁,13k我不知道在乌鲁木齐是一个什么水平,这个你在那个招聘网站上应该能看得出来。
如果是一个正常水平,那后期怎么规划呢,其实很简单,听我说你应该重点围绕我还是那句话,记住以大并发量和大数据量这两个核心点,围绕这两个核心点展开的系列下的系统化补充,在我们的课程里面的一个项目。
就会把这两个点全给你弄出来,就是大并发量和大数据量,然后围绕展开的点呢,我前面列了一下,简单跟你说过,我不知道你还记不记得大病发量来讲的话呢,就是高弹高扩展呃,各种的中间件分布式相关的理论。
微服务的落地与原生的落地,这是以大病发量为基础,而大数据量为基础呢,打开我们的大数据课,你从头到尾读完就行了,大数据的存储,大数据的清洗,大数据的调度,大数据的处理,大数据的实时计算。
p two p计算数据湖数据仓库,其实这两个核心,这两个核心点拿下,我这么跟你说,以你的学历和年龄,你应该在乌鲁木齐叫恒汤,啥意思啊,就是乌鲁木齐只要有机会,不管他的薪水是多少。
老师都有办法能让你拿到这个面试机会啊,当然能不能面过,是取决于你学习的深度细节程度,但是呢肯定能让你在乌鲁木齐横汤,这是没问题的,围绕这两个核心点听懂了吧,二三线仍是恒康啊,这个肯定没有问题好。
我看看还有没有其他同学有问题的,这东西有问题找老师问,我们家有12分钟,12分钟老师就下播了,不客气啊,150嗯,啊12分钟啊,12分钟老师就下播了,看有没有同学有疑问的,好如果没有问题的话。
那咱们明天见好吧,准备咱们明天见了啊,潇洒天下,咱们明天见啊,兄弟们,明天我给大家讲原子性啊,see you tomorrow,各位晚安好,谢谢大家,谢谢米斯特,299坦克车,summer出游。
thank you,拜拜。



什么时候潇洒天下,说什么时候能进行简历优化,你是报完名了吗,你报名之后就是你找工作的,大概前两周开始进行简历优化,这个是最合理的,不要太着急。

不要太早,因为如果你太早的时候,你你你简历里面还有很多东西没有没有学完,没有学完,你就写不进去啊,这个时候是最合理的。

不要着急。

答疑怎么办,对于呃我们是多个老师多对一的呃,多对一的意思呢是这样的,如果你有技术问题,你建议你提问,在咱们的问答系统上,正常情况下呢应该很快啊,我们追求的是一个小时,而实际当中呢有工作时间。
一个小时就会有有人回答你呃,如果非工作时间的话,可能要到24小时,最多最多24小时就会有人回答你啊,这是技术问题呃,如果是非技术的问题呢,是我们每一个人都有一个单独指导群,非技术问题呢扔在我们的群里。
老师就会给你指导,你就说一些规划上的问题,发展上的问题,可以简历指导多少次,这个不限不限数,你只要跳槽的时候,一般简历指导是这样的,孑然一身有限制吗,答疑范围啥的,答疑范围没有限制,山药复苏。
但是你要知道的是,在我们课程范围之内的啊,我们是有义务绝对给你答出来,但是在我们课程范围之外的,我们没有这个义务全部给你打出来,但是我们会会回答山药扶苏,听明白了吧,简历知道多少次,这是不限啊。
只要你什么时候有需要,他们说阿里裁员19000,那个我记得今年是哪个大厂,应该是美团还是谁说是裁员2万,但是招人招多少,24000还是多少,我忘了哈,您查一下,大厂都在不停地裁,也在不停的招。
要不然就不会刚才有那个47万进阿里的了,对不对,老师答疑多长时间回复,最长不超过24小时,正常一个小时以内,但我我现在很难保证,你就是说你你在大半夜的提个问题啊,一个小时真的回答你。
正常工作时间一个小时以内啊,如果你提的那个问题,比方说偏难,有的时候我们需要组织老师来研究,你这个问题走,所以这个时候最长不超过24小时,k美团扩招了,扩招1万多,我看小姐姐发美团开招1万人。
要技术多不呃,应该不少啊,具体技术有多少不太清楚,但肯定不少,想去,但是学历不好学,你什么学历啊,孑然一身,马老师这课性价比怎么样,欢迎你多做比较,你要能找出比我们性价比更高的来看,20课都可以不收钱。
送你就讲这个课,我我我们做这个课,很多时候没有太考虑赚钱的事情,就是赚钱的考虑的不多,我们整个整个那个培训的利润利润里面呢,就是说在我们整个呃做做培训的里面,我们是非常偏低的,你想想看。
可以用十几位老师来讲一门课,然后呢课时量呢是排名第二的十倍左右,你想想看它的性价比什么样啊,自己自己琢磨琢磨,美团队学历要求高吗,呃现在你要开发的话,那我估计二本以上就二本,包括二本,大专会难够呛。
然后三本的话呢,嗯偶尔的机会苦中苦人上人,web two太卷了,我three也可,我three也也卷,你不要认为我web three就怎么样,web 3的话呢,目前的工作岗大多数时候集中在什么呢。
要么是区块链方向,要么是那种ar vr这个方向一样的卷,中国但凡有不卷的东西,不是很卷,赚钱还高,你认为中国人会怎么样干,你好好想想,马上扑上去,所以找一个更宽的方向去卷,这个是大家避不开的,正视他。
也许你有冲上去直面它的勇气的时候,其实你就会发现没那么难,很多人是被吓死的,连战场都没有上,不要干这种傻事儿哦,还有问题吗,看看刚才我要下播了,都突然间有有一堆人在这问问题,321好,各位同学再见。
202

被折叠的 条评论
为什么被折叠?



