00:00
PC里面扔了条指令。Register里面扔了一些数据,那我就按照指令来计算就可以了。总而言之,谁来负责扔这件事儿?扔指令进去,扔数据进去,谁来负责扔,记住。这哥们儿叫做操作系统。呃,我争取给大家讲的浅显一些,不知道大家能不能跟得上。如果能跟上的话,给老师扣个一来。嗯。那下面的问题就来了,站在操作系统的角度,他会有一大堆的孩子嗷嗷待哺,大家都叫着,哎,到我了,到我了,哥们儿,能不能让我,让我执行了这么多线程,等着我执行,操作系统说,你到底该谁了呢?这个叫做调度策略。今天肯定来不及讲这个调度策略,现在Linux内核最核心的其实就是这个调度策略,我们先不去管它,总而言之,言而总之。
01:01
站在操作系统角度,不会说让某一些个县城他自己的孩子老饿着。也不会说。让CPU永远都是执行某其中某一个线程,其他线程都啊执行不到位,这个这个肯定不行。所以在实际执行的时候,一定是执行你一会儿。执行,你一会儿的意思就是把你的。指令,把你的数据扔到计算机里执行一会儿,执行一会儿之后就换另外一个人。这是一接待员,速度特别快,接待你一会儿,接待他一会儿,接待你一会儿,接待他一会儿,那现在问题就来了。我就想问你。假如在开始的时候,我。执行的是T1对吧。我这里面装的是T1的指令,T1的数据,那我现在要执行T2了,等会儿一会儿还回来又执行T1同学们。那你们想一下。我中间要做一个什么操作呢?必须要做的一件事,我一定得记录下来。T1。
02:05
执行到哪里了?就说我第一次执行T1的时候,我在哪里停下了,我下一条指令该执行的是谁,注意你执行T2的时候,他会把那个指令PC里面这个指令就已经把T1的给你给你给你冲掉了,对吧。T1的就没有了,这时候执行的是T2的,那T1的跑到哪去呢?一定得保存现场。所以这个呢,叫保存现场。啥意思,刚开始在执行T1的时候。执行到哪里了?数据是什么?这些东西在执行T2之前都给我保存好。好,这个一般呢会保存到catch里面,这个我们先不去管它保存到哪里去,总而言之,言而总之得把T1的现场先保存好。保存好之后T2来了,T2来了什么意思啊,这里面执行的是T2的内容,T2的内容,然后T2执行一会儿,T1要恢复执行的话,怎么执行。
03:04
把原来的现场做恢复好,关于T1 T2 T3 T4TN都是一样的。这部分内容叫什么呢?叫线程的切换。专业名字叫。指的什么?指的是CPU保存现场。执行新线程。恢复线继续执行原线程。
04:01
这样的一个过程,那有同学啊,可能就会说,老师这个你跟我讲这个到底有啥用呢,在这儿呢,有一道简非常简单面试题就是。也也是一个非常简单的问题,大家都知道啊,呃,如果一个任务采用单线程的时候,开始执行的时候,它的效率呢,稍微低一些,那么我们就会说我们用多线程来执行它,嗯,很多个人来帮他干这事儿,那现在问题是是不是线程数越多越好。是不是一定是越多越好,能不能说我是它一个线性,呃,这种线性效率提升的概念,就说我的线程数原来是十个,那我的效率提升十倍,我要是1万个,那效率就提升1万倍,会是这样吗?肯定不可能。绝对不可能,为什么?因为我说过CPU啊,它中间有一个切换的过程,听懂了吗?就是切换得把你把你放进来,把我放进来,你如果1万个线程的话,我要在这1万个线程以前来回切换,所以你整个把资源全浪费在线程切换上。
05:09
所以不一定线程数是越多越好。OK。我们再来稍微回顾一下我讲的这些基本的内容。看看大家是不是能理解。记得我画了个图。看看啊。超线程,嗯。呃,这里是一个小小的线程切换的概念。我记得我是做成了一个动画。哎,为了给大家讲清楚,费了劲了,这是两个线程,这是T1线程,它里面有指令数据,T2线程指令数据所谓的线程切换指的是什么呢?当我们执行T1的时候,T1的内容会装到我们的CPU里。当我们要执行T2的时候,T1内容会从CPU里面。挪出去,挪到catch里面去。好,T2进来执行。
06:02
OK,就是这样呢,就是一个线程切换的过程,这个动画没有演示出来,再回来这个过程我会进下一步进一步完善它。这是线程切换的概念线,明白了线程切换之后,你就会知道这个线程数量并不是越多越好。它会有一个合适的值,如果你特别多的话,它会把整个的时间全浪费在线程切换上。好了。我们回过回过头来看这张图。大家明白了现成的最基本的概念之后呢,我们回过头来看这张图。这张图呢,实际上背后有一个特别重要的问题,这问题是什么呢?注意啊,我在这里所写的线程,这个线程是什么?这个线程是一个。用户。所产生的线程就是我们我们的APP自己所产生的线程。但是现在的问题是,我们APP产生的线程一定和我们CPU运行的这些个线程是一一对应的吗?
07:06
听我说,在这我就讲到了go语言的最核心概念,同学说老师您您讲这个Java讲完还还讲构语言,没错。所有东西都是相通的。在老师这儿看来。无欲无我,大家都差不多。好好听,假如我们是红颜色的,原是我们用户级别的线程,假如我们这个颜色的啊,这哥们我换个颜色吧。嗯。好,假如这个颜色的是我们操作系统级别的线程,就是俗称的内核线程。
08:04
有同学说,老师,我的APP不能直接产生内核线程吗?听我说可以。呃,很多APP如果你写的是写的是什么呢?比如说你用C语言写的,你就直接调用了内核的接口,那你直接产生的就是内核线程,但是也有一些应用程序,什么应用程序呢,比如这边M。GVM是一个虚拟机啊,虚拟机里面是我们Java写出来的那个线程,好,Java写出来那个线程。和我们的内核线程又是一种什么关系呢?构语言里面大家都知道,我不知道有没有同学听过这个概念啊,这个概念呢,叫做呃,携程。这大家对有多少同学对购物员比较熟的,有吗?有的给老师扣一嗯。听不懂是吧,啊polaris没有听懂啊,Sorry。
09:01
呃,现成的最基本的概念,我觉得大家伙都清楚。就是一,一个程序里头不同的执行路径,它并不是说从头12345这么执行完的,而说并行执行。而构语言里面,你们知道是怎么起一个。这种现所谓的现成的概念,单独起一个执行执行路径的吗?我看我购物员有没有打开在这里。听我说啊,你你你你,你不用心焦,也不用着急,因为我讲课呢,我喜欢纵横百阖,把那个横的竖的都给大家讲清楚,你明白了这些个最基本的概念之后呢,其实任何语言对你来说也就是点语法的区别而已,好好听我说。作为构语言来讲。大家看这里,这里呢,我是写了一个小程序,这小程序呢,有一个客户过来,我会用一个方法,这个方法叫handle去处理它。具体的逻辑是什么?你不用管,你就可以想象成为这是一个餐厅。
10:04
有一个客户进来的话,我就派一个服务员去处理他。假如我们前面不加这个关键字,那么相当于你的餐厅只有一个服务员,来多少客户也只有这一个服务员去服务他。但是如果你在前面加了这个关键字,Go就相当于。有一个客户过来,我就有一个新服务员去服务他,又有一个客户过来,就有一个新服务员,这里头非常像什么,非常像Java里面的现成的概念。现场的概念。就是有一个客户端连接过来,我就起一个线程去处理它,有一个客户端连接,我就起一个线程。只不过在go语言里面,它这个不叫写,不叫线程,叫什么叫go routine。叫做构语言的携程是这个概念。携程。
11:01
OK。而且呢,它的这种起携程的方式,或者叫起多线程的方式,多方位处理的方式,比Java写起来超简单多了,就直接一个关键字就搞定了。这是构言,叫做天生支持高并发,听懂了这块还这块大家还能跟上吗?嗯。对,你就想象着,原来呢,我们叫起一个线程,你有一个thread。调它的star的方法,而在go语言里面,你只需要加一个关键字go,后面跟个方法名就可以。不用控制总量的,对,不用控制总量,我一会儿告诉你为什么不用控制。睡着了是吧,语法堂而已。不能这么理解,大哥,这真不是一份汤。你说出这句话来,我只能说你是一一瓶子不满,半瓶子晃荡。好。
12:00
扯得有点远了啊,注意看,这里面有个概念,叫做用户级别的线程。和操作系统级别的线程,内核级别的线程,它到底是一个什么样的对应关系?好好听我讲,作为Java来说,站在JVM的角度。一个Java的线程对应一个。内核线程什么意思?就是1:1的一个关系。Java就是这样做的。你在Java里面,你有一个thread出来。站站在那个那个那个内核的角度,它就对应的给你起一个内核级别的线程。大家记得这个这个结论好吧,再说一下就是操作系统管理的线程。和Java线JVM所起的这个线程是1:1的一个关系。那有同学说,老师你这个是1:1,那构言里面那个是什么呢。构语言里面这个是M比N的关系,而且M远远大于N,它是一个M比N的关系。
13:06
好,这是什么意思?好,听我讲。我看这个图是不是画了,我记得我有印象是画过,画过我就不重新画了啊。看这里。这是购物园里面的模型。勾圆面是这么一个概念。当我们起一个构语言的程序的时候。它会自动化的给你起来一堆的内核线程。就是内核,县城已经给你起来了。好,假如在go语言里面起一个斜程,就是我们go handle go handle来一个,来一个我就起一个,来一个我就起一个,这个携程是个什么概念呢?这个携程就是这么一个东西。这些携程起来之后,会放到一个一个的队列里,好多好多队列放在这里。然后交由下面的这些内核线程去执行。
14:06
所以这是go的携程的概念,Go携程更像是一个任务,一个task。你不是要求我处理这个任务吗?我就把这个任务弄出来之后扔到队列里,谁去处理?一堆的内核线程去处理。内核现场。而Java里面线成什么样,大家还记得吗?同学们,Java里面线成什么样?Java里面是来一个我内核就对应一个线程,来一个对应的线程。那么刚才我们说过。站在操作系统的角度。如果说我管理的线程数特别多的时候,我的效率反而会降低,所以我们说Java里面你要写东西的时候。不能写特别特别多的线程,你要来1万个线程,我告诉你,你整个程序快崩了。他所有的资源都花在线程切换上了。但是勾语言里面你随便玩,不限制数量几万个没问题,有的人说我写几十万个,它是不是也会崩,也会,但是毕竟这个容量比Java这个要大好多,原因是什么?原因是它真正执行程序的时候,实际上就那么十个,几十个线程。
15:15
而这个任务都是在这里好好的队列里头等待着,等到我执行你了,你就来执行,不到执行你的时候,你就你就跟我这等着。所以同学们,你们想象一下,我不知道大家有没有接触过线程池,Java里面有线程池的概念。线程池是一个优先启动的一堆线程,然后呢,这里面装着一系列的任务,线程池里面呢,去往这个任务队列里头去拿。拿拿任务,拿完一个我就执行一个,拿完一个执行一个。拿完一个执行一个OK。所谓的Java的,所谓的go里面的携程的概念,Go routine。非常类似于Java里面的线程池。
16:05
我只是说非常类似。但是并不是完全一致。不完全一致的原因,我尝试讲一下。有点深。不知道大家还能不能跟得上。能跟上的给老师扣个一。嗯嗯。好好听我讲啊。呃,我讲完你Java和go基本上就全全全明白了,甚至包括后面的rest基本上也就全明白了。好听讲。那么。我们说Java语言里面呢,也有线程池,尤其是它的就是这个线程池叫。它的模型呢,和这个图非常的类似。每一个线程都有自己的任务队列,自己的任务队列完全没问题啊,执行自己的任务队列执行完了之后呢,再合并,怎么怎么怎么样,很类似,有好多个任务队列,每个任务队列里面装着好多任务。
17:08
但是。任务和任务之间,他们是不能做同步的。就是说不能说我这个任务执行完了之后,你才能执行另外一个任务,好这件事儿Java是做不了的。但是构元里面做了这么一个事儿,他做了一件什么事呢?就是任务和任务之间,这哥俩是可以做同步的。我们之间说我执行完了,你才能执行完全没问题,我执行一会儿,你执行一会儿,你执行到什么程度了,到我执行完全没问题。Java是控制不到这一点的。同学们,你们站在构言的角度想象一下,这一个另外一个东西不就是一个线程的同步吗?所以这就是构元里面的线程。它中间是要做一些同步。他怎么做到的?其实模拟了CPU的原理。
18:00
CPU大家还记得吗?不就是说把某一个里面的数据放进来。放到我们CPU来来执行。然后如果你你你到你你执行,你执你执行完了,或者执行到一段时间了,或者阻塞了,把你拿出去做个备份。把另外一个拿进来继续执行,所以它是模拟了在用户空间,模拟了CPU的执行。这块我不知道大家听懂没有。所以不能完完全全不能直接说说它就是相当于呃,Java语言里面的线程池,就是Java里面的任务,就是一个语法堂。真不是那么回事儿,这里面是有一个切换的概念。怎么模拟大哥?你问的有点太多了。这要讲起来就真没完了,怎么模拟?拿程序模拟,你Java虚拟机都能模拟,如果你想好好听这块,我讲的Java虚拟机的内容,认真听。
19:04
就能理解。就是一个站,用站来模拟就行了。好讲到现在为止呢,基本上把腾讯问到的问题好听我讲。这是你听不懂也正常,因为这里讲到的问题是腾讯大概在70万左右的一个程序员问到的现在的这样一个深度。嗯,听不太懂呢,也是非常的正常的好吧,大概是腾讯70万左右的水平,他会问到,呃,整个的这个先城到底是怎么回事。县城和县城的区别又是什么?好,你把这个图画给他就全明白了。好了。呃,大概的意思好吧。我从底层开始,慢慢的一点点的讲给你听。每一个每一个这个这个这个这个这个概念啊,我大概给大家讲了这个现成的这种模型啊,House模型呢,大概是1:1go的模型是MBN,你写可以写2万个携程完全没问题啊。
20:12
当然,Rest语言是史上最牛逼的语言了,这个我今天不打算给大家聊这个,这个聊起来又没完了,呃,毕竟呢,我们今天聊的是Java的多线程。但是有同学可能到这儿可能就会问了,说老师Java里面有没有携程?Java有没有携程啊,就是你我我我我我已经有任务队列了,我任务和任务之间只要能做切换,是不是Java里面就可以支持携程啊。好,听我讲Java里面呢。并没有天然原生的携程,但是呢,有各种各样的类库可以来扩展它。大家伙听到过的,有可能是这个叫craz。还有一个就是阿里内部直接用过我们曾经讲过的,就是也是阿里的程老师给大家讲的。Killing。这个部分的内容就是本身他们在阿里内部就直接这么用的,K这块课程也都已经上线了。
21:08
好。嗯。嗯,现在Java呢,还并不是原生的就支持啊,也许在后面的某一个版本里头。它会原生的支持,那么构语言相对于Java语言的执行上的优势基本就没了。OK。这些基础内容讲完之后呢?下面我们来聊一聊锁。线程本身呢,不是特别难,其实难的就在于线程之间的同步了,解锁这个概念。好,我们可以继续吗?可以继续给老师扣一,你们还能坚持住吗?嗯。好,我们来聊一聊锁这个概念,好好听我讲啊,锁这个东西是一个什么概念呢?它是一个逻辑上的概念啊,它并不是。
22:07
一个那种一定是某一种写法上的一些概念,大家千万不要这么来理解。所是一个什么内容?锁是这么一个概念。注意,第一所是一个逻辑概念,它是一个的概念。所的意思是说呢,要保证一个数据的一致性。必须要采取一些的一些手段,啥意思呢?什么叫数据一致性呢?比如说你要干一件事的时候啊,你要你要你要你要你你你要进去干这件事。那么同一时刻。访问这个马桶的,注意只能有一个人。不可能有人坐你腿上,然后再来一个在你马桶,在在在在你身上同时干这事儿,那你这个马桶就属于处于不一致的状态,这个就就是这个意思。
23:04
总而言之,言而总之,就是在某一个时刻,只允许一个线程干这件事儿,好,这个时候我们就需要干嘛呢?需要上锁。注意这个锁的概念呢,是这么一个概念,锁是一个单独的东西。你可以,呃,你干任何事情也可以用任何一把锁来进行锁定,只要是大家伙做同步的时候,锁定是同一把锁就行。从形象上来理解啊,假如说有一哥们儿,这哥们儿叫。这哥们儿叫谁啊,我看看。欧拉,欧拉欧拉啊,欧拉欧拉已经看饿了对吧,那就他了。他呢,要进来干这件事儿,他进来干这件事儿的时候,如果说这个马桶大家伙都可以抢。那么为了保证在同一时刻只有一个人能抢到,我们就需要干这么一件事,就是进马桶干事儿的时候得需要。持有这把锁才可以。什么叫持有这把锁?
24:01
就是把这个锁给锁上,那有一个门口给锁上。那另外别人要来的时候,怎么就是你,当你当你锁定的时候,欧拉欧拉已经进来了,他不是饿了吗?他进来了,他进来了之后呢。锁定了这把锁。然后当别人又有新的人要过来的时候,同学们,你们想象一下,那这个时候该怎么办呀,又有新的人要过来的时候啊,啥情况?OKOKOK,好嘞,嗯。啊,刚才有点小事啊,我们同事过来了一下,稍微打断了一下,我们回来啊,如果有人已经持有这把锁了,持有这把锁概念就是已经把它给lock住了。已经给他锁住了。好,持有这把锁的时候,他就可以进去干这事了,那别人来的时候怎么才能干上这事呢?只有一个办法,就是等这把锁打开。等他不持有这把锁的时候。撬锁肯定是不行的。
25:00
当然通过通过程序上的概念叫做什么时候叫持有这把锁呢?我们一般是这么写的。代码。Synchronized。什么什么?然后所谓的干一件事就是。整个这个大括号里。拉屎拉屎拉屎拉屎拉屎拉屎,噗噗噗噗噗。好,什么事叫释放这把锁呢?释放这把锁就是整个这里面的事儿干完了。大括号结束的时候,OK,这个时候这把锁放开。那什么时候别的人进来呢?有另外的人哥们儿过来,发现这把锁是开的,他就可以尝试去锁他,锁住了,回他了。这是这把锁的最基本的概念。好,这就是锁的最基本的概念。当然那个从语法上角度上来来讲呢,说这把锁你锁定的到底是谁,其实你锁定的是谁,由你自己来指定,我我不知道大家伙的这种这种这种基本的。
26:06
概念到底是一个什么程度啊,我我问大家几个小小小的问题吧,比如说嗯。我们我们我我我简单给大家写几个事例啊,看这里,比如说我这么写,Object等于new object。好,那我要是,呃这样写的话,好,谁是那把锁,这把锁是谁。As all。对,就是扭出来这个object,这就这个就是那把锁,听懂了吗?这就是锁定这把锁,这个这个对象就被我当成锁来用了,当然在这个理念的话呢,实际上是你在呃里面输出的任何的内容啊,噗噗噗噗噗,你干的一些任何的事情,这个呢,就是当你持有这把锁的时候,你干的事。保证在同一时刻只会有一个线程。来干这件事儿,放心,不会有第二个线程,必须得得等你这个线程执行完了,释放了这把锁,别的线程才能进来。
27:02
这个在整体上就能保证数据的一致了,那我问大家几个简单的问题啊,我想问大家这么一个问题,那么假如呢?假如我有一个方法。我是这么写的,Public synchronized,好,请大家告诉我。好,请大家告诉我,这里谁是那把锁?谁是那把锁?OK,这相当于什么?相当于锁定的是this对象synchron this,所以this锁OK。那现在我再问大家一句。假如我这么写。
28:02
好,这时候谁是那把锁?Who?对,这时候写的,这时候这把锁是什么?相当于是这么写的synchronized t。03TOM。Class。呃,这个比较基础。不需要不需要我专门解释吧,嗯。好,既然大家理解这一点就行啊,就就是你凡是你看到SYNCH关键字的时候,你放心肯定有一把锁,那么这把锁是谁,那就看你锁的是谁,你写的是synchron o,那就是那个O,你写的是SNCH的方法,那就是this,你写的是SYNCH静态方法,那就是。当前的class。好了,我们可以继续了吗?可以继续,老师扣一。所以大家一定要理解,确确实实在你写完代码之后,真正的有这么一个锁对象存在的,你记住这一点。
29:01
然后当然有一个专业名词呢,下面你要干的一些事儿,这种在专业上我们称之为叫critical section,叫做那个呃,临界区,这个我们先不去管它,临界区的概念就是放心,总而言之,言而总之,同一时刻一定只有一个线程在里边,不会有另外的别的线程,只有等这个线程执行完了,别的线程才能执行好了,这个就叫上锁,这就是锁的最基本的概念,所有各种各样的类型。乐观所、悲观所、读写所、分段所等等等等,但是我们先要理解这个基本的概念开始。啊,我看有同学说那个,嗯,感觉还没有深度讲普及还可以啊,大局为重,嗯,别着急。还是那句话啊。得从得照顾基础稍差的同学,大家觉得有收获吗?
我来说两句