00:00
好,那么接着的话呢,咱们来看一下这个零三啊,涉及到我们这一章里边第二个重点,第一个重点的话呢,就是前面讲的如何去创建做线程两种方式,然后第二个重点的问题就是关于程的安全问题,哎,如何解决线程的安全问题。好,这个呢,也算是咱们这章一个难点。首先的话呢,咱们通过一个题目呢,来去说这个题目的话呢,是我这儿呢,写了对应的一个例题,在我们这个,诶课件里边呢,这块也有写。找到我们这块。这儿呢,就提到了叫现成的安全问题,诶首先的话呢,我们先看一看怎么叫现成的安全问题,稍微的理解一下。啊,这呢这样说的啊说呢,当我们使用多个线程访问同一个资源的时候,可能呢,是同一个变量,同一个文件,或者呢是同一条记录。在多个线程去来访问了,那我们说呢,如果多个线程只有读的操作,那就不会涉及到线程的安全问题,因为你不会对这个数据呢进行修改,那一旦要修改的话呢,这时候就可能会出现一些问题了。
01:03
啊,举个例子,比如说这块呢,你有个账户,你的账户呢,是有3000块钱。哎,说现在话呢,你呢,想从这里边取2000块钱。你这块你可能是拿你这个银行卡到这个ATM机上呢,你想去取OK,然后呢,同时的话呢,你媳妇儿这块呢,可能她就她的淘宝就关联你这个账户了,是吧?诶他呢,这块也想买个东西啊,也需要花2000块钱。好,那这时候我们说可能会出现一个问题哈,呃,你要说你这块呢,先取了这2000了,也拿走了,这呢变成1000了,然后你媳妇这块呢,再去取2000,一看不够了,这就没支付成功,这个呢都是很正常的,但是呢,可能会有这样的一个场景啊,我们就细节来分析一下。把这个图往下一点,老师在这晃是吧,嗯,你现在的话呢,先要判断一下你的账户余额,3000块钱,你要取两千一判断满足。满足的情况下呢,我们现在呢就要去取了,但是注意这时候呢,还没有执行这个冲减的操作呢。
02:02
但是你已经满足这个依附条件了啊,诶就要去取了,然后就在这一刻,然后你媳妇这块呢,她也去要取2000块钱,他呢一判断这时候因为你还没有减这2000块钱,所以还是3000。一判断呢,发现呢,也满足取2000的条件,所以呢就也进入这个依附条件了。然后接下来你俩都在这个衣服里边了,相继是不是就取了2000块钱出来了。诶取完之后的话呢,这个账户呢,就变成负1000了。本来是个储蓄卡。变成了信用卡完成了是吧,哎,相当于这块呢,就成了一个负数了,哎这呢其实就是我们所说的叫县城的安全问题了。啊,现场安全问题啊,这呢,我放了一个生活中的图,没有直接关系哈,就是看似呢挺安全的,其实的话呢,呃,跟没有上锁是一样的是吧。行,那这个就回过来啊,那下边的话呢,咱们通过一个题目来说明一下这个线上的安全问题,以及呢,如何来解决这个安全问题啊,这呢说的说火车站呢卖票,咱们呢模拟一下这个卖票的过程啊,疫情期间这个火车的话呢,只卖100个座。
03:10
只卖一百一百张票,咱们呢,开启多个窗口呢,去模拟这个售票的一个过程啊。好,那么回过来,那其实呢,就是这道题目了。啊,咱们这儿呢,开三个线程啊,开三个线程,我们来做这个事儿。好,那咱们就在这个not safe这里边儿呢去实现,首先这块我们写一下啊。嗯,这个实现的话呢,我们就写一个啊叫window,哎,关于它的一个测试。哎,我就这样写了啊。好,那我们这儿呢,先写一个。继承还是实现呢?都可以是吧,先写实现行。实践的话呢,我们这块呢,来首先呢是一个class。哎,我们这块呢,是想卖票是吧,咱比如说叫sell ticket吧。让他呢,去一下这个random。好没问题啊,那么这块的话呢,涉及到这个方法的一个重写ctrl I。
04:04
哎,点一下。没问题了,现在我们要卖票,一共呢是有100张票,那这里边儿首先涉及到一个变量的定义。啊,叫做ticket是吧。100张票,你说这个我写这儿合适不?哎,大家可能没发现是吧,你这样看哈,我们这块呢,如果写完了以后,下边要做的事呢,就是一个没方法。要创建这个对象是吧。比如说叫S了啊。好,创建完这个对象以后呢,接下来我们使用这个实现的方式嘛,就把这个S呢,是不是就放到thread这个构造器里了是吧。Sad这个我们来个T114年不是有三个线程吗。然后呢,这三个线程的话呢,我们就称为呢叫三个窗口了,那不妨这块呢,我们就给它呢去起个名字了啊set一下这个name,这个我们叫窗口一。以此类推。哎,窗口二窗口三。
05:00
OK,没问题,好,然后接下来的话呢,我们通过这个T1呢,咱去调这个四大方法。那以此类推。T2T3。好,那么整个这个程序呢,跑起来以后呢,就各自来调这个run了。三个线程来调转,那你想象一下这块呢,是不是有300张票了,就。对,所以放这儿呢,不靠谱是吧。放那儿不行,那怎么办?哎,这块我们会想到说那就往上提。哎,把它那是不是提到这个位置啊。那这块呢,是100张票吗。还是说还是300张。100张。呃,因为呢,咱们就创建了一个对象是吧,哎,它被三个线程呢所共享吧。诶,所以这块呢,诶就没问题了,行,那在这里边儿呢,咱们就开始呢,去售票了啊,那售票的话呢,每一个县城呢,不一定呢,就受他的1/3。这都不好说的,所以这块呢,关于循环多少次呢,我们也不确定是吧。不发呢,我就写个叫外处吧。啊,在这里边儿呢,他就一顿的去执行,诶只要呢,你判断一下这个T呀。
06:05
它是大于零的,就说明还有票是吧?哎,咱们这呢就写一下叭,如说哪个窗口买票了,卖的这个票号是多少,哎,咱们就这样来写了啊,哎输出一下。哎,点current th.get个name啊,比如说叫窗口一是吧,哎售票。售票,然后呢,票号为。诶这个呢,我们就倒着来吧,一开始这个TK呢是100,诶这呢,我们就先把这个100号这个票呢给它卖了,然后呢,紧跟着的话呢,这个TK呢,你得减减一下。哎,就得这么着是吧,好,那如果你这个7K的要是等于零了,说明呢,这个票呢就卖完了,哎我们就退出当前这个循环就可以了。这个清楚吧。那就这样的一个过程。行这呢,是不是就写完了就。就这样来写,来我们这块呢,去做一个run执行。OK,哎,这块你发现了这三个窗口呢,这块都会去卖票啊,是有这种场景的,然后呢,我们稍微的看一看。
07:07
诶发现的话呢,诶有点问题是吧。三个窗口都卖了100号这个票了。这呢,咱们才卖了一百一百张票,就出现了这个叫重票的问题是吧。这个呢,其实也是我们所谓的一个叫线程的安全问题了,不应该出现这样的问题的。那其他的话呢,也有可能会出现重复。啊,最后这块呢,还倒没事儿。啊,这样啊。好诶这块呢,可能有同学会发现呢,咱们整个这个打印这个票号的话呢,不是严格的一个递减是吧。你看这块还出现这样的场景了。这个能理解不?就是卖票的话呢,他肯定是西安卖的这个票号大的。对,后面的票号小的,只不过呢,这个输出呢,咱们需要呢,显示到控台显示的时候呢,就是肯定是他二号窗口先卖这个十号票,然后一号窗口才卖的这个九号票,但是显示的时候呢,因为咱们需要呢,以一个维度,你想这呢是三个线程啊。
08:01
你这块往这呈现的时候呢,就相当于是不是就得变成个有点像单线程一样,都显示到这儿是吧,所以说显示的时候呢,它就把这个呢给显示到下边了。这个其实还好。这个不用呢,特意的去关注这个事儿啊。但是呢,我们这里边儿出现了一个100号的票,有好几张,这个是显然不对的。啊,这个是错误的,那我这块呢,稍微的再调一下呢,可能大家还会看到这种错票的场景,比如说在这个位置啊。或者加一个sleep来一个十毫秒。诶,给大家来一个开啊,1.print,好,我们再去做一个run。大家看。这儿呢叫重票,这呢还出来一个负一号票。这个呢,也接受不了。哎,你要说呢,这个两个都是一号票吧,这个还能忍了,大不了这俩人就坐一起是吧。你整个负一号票,这这往哪坐呢。是吧,挂到外边是吧,啊这这接受不了了就哈,所以这个呢是绝对不能出现的,但是现在出现了,这呢就是现成的安全问题。
09:09
啊,它的安全问题行,这呢是咱们使用这个叫实现的方式,出现了这样的场景,那么继承的方式会出现吗。哎,其实也会。啊来,咱们拿个继承,咱们也写一下啊继承。哎,看了啊这块呢,不妨我就要个window,哎,Test咱就一吧。那就它啊,这个我们在这稍微说一下啊使用。记忆成thread类的方式。哎,这个呢,实现卖票。OK啊,然后把它呢,我们在CTRLC啊,这个粘到这儿。哎,使用实现接口的方式。哎,接口的方式呢,实现买票OK啊好,那我们来看一下这个继承这个技能的话呢,我们在这儿写啊。
10:02
首先呢,这块不妨呢就叫做window了,让他呢去继承于咱们的TH。形成以后的话呢,这块我们就开始呢,要重写这个run方法了。哎,在这去买票,然后后边这个过程呢,跟咱们刚才那写的这个实现呢,有点像了,我们呢要创建三个窗口是吧,那个run方法咱们等会去写啊。一个W1吧。哎,你有一个。哎,这么着,然后呢,一共是有三个。哎,那么三个window呢,其实就相当于我们这里边儿三个线程。然后下边呢,我们去给他们去依次呢去命名一下啊。这个呢叫窗口一是吧。诶,窗口二窗口三。诶没问题,好,然后呢,我们再分别呢,让他们去调一个四大方法啊,启动起来。诶这呢就妥了,好,当调完这个四大方法以后呢,就都来执行这个run方法了,那这时候就开始在卖票了。
11:00
哎,卖票这块呢,首先涉及到这个票的问题,同样的不能定义在这儿。那我们就考虑定义在这儿对吧,Int的一个叫T等于100啊这块你要想这样写还行不行啊。对同学感觉有问题,咱们先等一会儿来处理好,那这里边儿呢,就去卖票了,这个卖票的这个细节的话呢,跟我们这儿呢,其实是类似的是吧。诶,我就把它那CTRLC啊,直接呢,咱们就粘过来。啊,这人就卖票了。好这呢写完了啊,写完以后的话呢,我们这时候呢,就想着去做一个执行来run起来。嗯,这块的话呢,你会发现呢,呃,也有问题,首先呢,你看这个重票的概率呢,是极高的啊,其次的话呢,你似乎发现呢。还真是每一个都冲。哎,你再细算一下呢,其实你发现这个票呢,明显的有点多。这时候其实卖了有300张是吧。哎,为啥是300张呢?
12:00
对,这个呢,就得用咱们前面讲的面量对象来解释了啊,这个类里边呢,我声明这个叫实例变量是吧,实例变量意味着每个对象是不是一份。那我们现在三个县城三个对象,三个对象呢,卖票各自卖各自的。这不就300张票吗?诶,所以这块呢,首先票数呢都不对哈,那一个车厢呢,本来就卖100张,人家刚才呢有一个重票,有个错票,其实还这样一笔的话呢,这个问题还算小了是吧,你这块呢,直接干了300张票啊。所以呢,要想解决这个问题怎么办呢?只能有一份是吧,哎,这边可能我们比较直接的话呢,就会想哎加一个static。哎,变成个静态了,这时候呢,就100张票了,好,这时候我们再去转一下。哎,100张是吧,啊,你大体一看的话呢,其实应该基本上就100,只能叫100左右是吧。诶,还会出现呢,跟我们实现的方式一样的这种重票,哎,最后呢,你发现呢,也会有这种错票的场景。这呢,也是不应该出现的。好,那这呢,就首先咱们呢,继承也好,实现也好,两种方式呢都写了,但是呢,都会有一些安全问题。
13:07
啊,那么咱们这里边儿呢,发现呢,出现了重票和错票啊,那么这个错票是怎么出现的呢?诶大家呢,看一下我这个PPT,稍微的咱们来给大家去做一个解释啊。来看一看这个原因。好,那么理想状态呢,就我们现在有三个线程吧,不管你是继承也好,实验也好啊,真的就三个窗口,然后呢,我们就去取这个TK的当中这个票号了啊,诶在转方法里边,我们判断一下它呢是否是大于零的啊,只要大于零我们就打印相关的这个票号就行了,但如果让你这个票呢,已经变成零了,这块呢,我们就执行这个else里边的break了,相当于呢就结束了。这是我们希望看到的场景。诶,但是他会有这种极端的状态。极端状态,比如说我们以这个错票呢,给大家去演示。现在的话呢,我们就剩一张票了。这个票号呢,就是一按说呢,就只能在卖一张。
14:01
哎,但是这时候呢,我们这个T这个线程呢,比如说它呢,ETK的大于零,一判断诶是个处。它就进入到我们这个义结构里了。他进到if结构里边以后呢,咱们加了一个sleep。相当于呢,它就进入一个阻塞的状态了。他没有真正的实现这个卖票减免呢。它阻塞了,然后在阻塞的比如十毫秒当中,是不是这个线程T2呢就进来了。P21进来发现呢,TK仍然是大于零的,它也进入衣服里边了。它也阻塞了,然后那个线能T3的话呢,它也进来了。他也阻塞了,然后之后的话呢,他们相继呢,都结束阻塞,但是呢,已经在衣服里了。这就意味着他们都会去卖票。哎,那你这块再减减,不就出现这个叫零号票和负一号票了吗。对吧,诶这个呢,就咱们说的这叫县城的这个安全问题了。哎,回过来,其实呢,对应的就是我们说的这块,你看第一个县城,哎,进来以后到这儿呢,给阻塞了,现在就剩一张票了啊,第二县城这不是也进来了,第三个也进来了之后呢,他们相继的都醒了,醒了以后呢,就把这个呢取出来以后,比如第一个线程把一呢打印了,减减了,然后第二呢再去打印的话,不就变成零了吗?再减减第三再打印不就变成负一了吗?
15:17
哎,就出问题了。啊,那么把我们这里边儿出现的这种重票和错票,咱们就称为那叫县城的安全问题了。来我们这块先做个总结啊说呢多线程卖票出现的问题,诶,我们说那叫出现了叫重票和错票。重票的道理呢,其实跟我们刚才说的是类似的。能理解吧?重票。这块呢,比如我们打印就是第一开始的时候呢,一进来不管它阻塞不阻塞啊,这块呢,我们把这个100呢,就卖了这个票号了,然后呢,他还没有及时的去减减呢。另外一个线上一过来呢,是不是一打印也是100啊。哎,这就是重票嘛。诶OK啊好,出现了重票和错票,然后呢,我们要分析是什么原因导致的呢。
16:06
为啥会出现这个呢?应该怎么样呢?是吧?诶,其实这块呢,就在说怎么着,我们就能够避免重票和错票了。嗯。不让别人操作了,对你看啊,咱们刚才呢,在操作这个TK的时候,哎,其中一个线程呢,判断了。判断了,然后呢,他还没有去接着执行后边操作的时候呢,另外的线程就进来了。诶,然后呢,咱们就以俩现状来说,其实也能够模拟出来这个重票和错票啊,诶我这块呢,就剩一张票了。正常来讲的话呢,应该是我判断大于零的时候呢,我得把这张票卖完了,我减减了我然后呢,把你变成零的时候呢,你再进来判断是吧。现在呢是我呢,刚判断完,我还没执行呢,你呢也进来了。
17:04
仍然出的问题。哎,所以这个原因呢,我们就归结为呢,就是呃,这个叫什么呀。线程一要操作这个TK的啊,过程中尚未结束的情况下。呃,情况下啊,这个其他的这个县城。啊也啊,参与进来。啊,对这个TK的呢,进行一个操作是吧。啊,这呢就导致了我们线上出现安全问题啊,重票和错票了,这块呢,明确注意一下,就是尚未结束啊好,那么该如何解决呢。哎,怎么就能不出现这个问题了呀?哎。必须呢得保证对,比如说我们这个线程一啊,他在操作这个TK的时候呢,得保证他操作完以后,其他的先生你再进来操作是吧。这样才行啊,所以这块呢,如何解决呢,哎,必须保证。
18:04
啊,一个线程在操作这个TK的使啊,或者叫操作TK的过程中,那其他线程。啊,其他线程必须等待啊,直到。哎,知道,比如说我们这个必须保证线程。嗯,假设叫A吧。哎,必须保证一个线程A,在操作TK的这个过程当中,其他线程呢,必须等待直到呢线程A。操作TK的结束以后。哎,那么其他县城才可以进来继续操作体啊。诶,进来。看继续。哎,操作这个TK的。啊,那么这里边儿这个7K的呀,就是大家多个线程共同来操作的一个数据,咱们其实就可以把它称为呢,叫。
19:01
共享数据。哎,这叫共享数据了,哎,其实你要这样来说的话呢,大家课间的时候呢,去这个厕所是吧。测的话呢,这里边一个一个的小小房间是吧,这是不是也都是共享数据啊。就拿一个来说吧。哎,现在呢,你这块呢,进去了。进去以后呢?你这一顿操作是吧。还没结束的时候呢,别人不能进去哈,别人要进去了呢。你俩在里边儿呢?就不安全是吧?啊,这就是县城的安全问题啊,必须得怎么着呢,你进去之后呢,然后呢,诶别人这块呢,也想去,那不行,你们都在这儿等着是吧,排一队啊,然后当你这块出来之后呢。诶,他们才能进去啊,你们就抢吧,是吧,你们再进去,所以这样的话呢,才能保证这个安全问题啊。好,那这呢是我们生活当中的一个例子了,那咱们这时候就得看,诶Java呢是怎么解决这个问题的。哎,怎么保证这个事儿的。
20:01
哎,这块我们就提到了,叫哎线程的同步机制啊。使用诶现成的同步诶机制啊来解决这个安全问题,那具体来讲的话呢,我们其实要讲两种方式,第一种呢,叫做同步代码块。哎,第二种呢,我们称为它叫同步方法啊。诶这样两种方式,诶其实这两种呢,本质上来讲其实是一样的啊,那咱们就先来说一下,这个叫同步代码块,这呢我们先把这个结构呢写出来,首先呢,对应着一个关键字叫sron的。你看这块我在写的时候呢,我一般就不说话了。因为这个单词呢,实在是不太好写啊。哎,大家呢,这个单词也要自己能够写出来。啊,你别呢,回头在代码中,我一写个SY自动一提示一回车出来了是吧?诶万一要是笔试当中呢,你得回手写啊叫SNCH啊SYNC是on。这个啊好,然后呢,这块有个小括号,一个大括号,这个小括号里边呢,放的叫同步监视器啊。
21:03
然后这个大括号里边呢,哎,我们把它称为呢,叫需要。啊,被同步的代码。哎,就是这样的一个意思啊好,那我们显然呢,得需要做一个说明了,哎,这里边说明呢,我们就一个来啊,首先啊这块我们先提一下这个事儿吧。就是把哪些代码呢需要给它包起来,这里边儿提到的叫需要被同步的代码。需要被同步的代码,哎,我说呢,即为啊叫操作。共享数据的代码。啊,在咱们这个卖票的这个程序当中,共享数据呢,其实就是T了。那就是凡是操作TK的这个代码呢,都叫做需要被同步的代码。啊,这块我们稍微的也解释一下啊,怎么叫这个共享数据。及哎多个县城。哎,都需要啊操作的这个数据。哎,比如说哎,咱们这个问题当中这个ticket。
22:02
诶大家卖票嘛,都会对这个变量呢,首先做一个判断啊,是不是大于零,包括呢,我去输出它,包括我们去减减它,这个都算是对他的一个操作。啊,操作的都是它,所以这就是共享数据。好,那我们需要呢,把这个共享数据呢,操作共享数据的代码呢,用这个大块呢,给它包起来。哎,包起来意味着什么呀,意味着就是这段代码呢,就是一个整体了。那就意味着,如果呢,一个线程正在操作啊,这个需要被同的代码的时候呢,别的线程都得等待。直到呢,我们其中的这个线程呢,把这一段代码操作完以后。哎,别的现场的你才能进来的去操作。啊,这块我们相当于在这再说一下啊,这个理解呢,就是说需要被同步的代码。哎,或者我们也可以理解成呢,叫操作共享数据的代码啊,这个呢,在。哎,在背啊,我们这个S的。
23:02
哎,这个。这叫什么,哎,包裹以后啊。就使得。啊,就使得。啊,一个县城在。哎,操作这些代码的过程中。哎,过程中啊,其他呢,县城必须等待。哎,这样的话呢,咱们就保证了这个安全的一个情况了。啊,这段代码只能是由一个线程都执行完以后,别的县城呢才能够进来。啊,这是我们做的这个事儿啊,那么操作这个事儿呢,是不是就完事了,怎么还有一个这个东西呢。哎,这个呢,我们称为它叫同步监视器是吧。诶,这个同步计然器,它这个作用呢是什么呀,就是它呢来决定说哪个线程来执行这段代码。换句话说呢,就是谁获得了同步显然器。谁指的就是哪个线程啊,哪个线程获得获得了这个同步监视器,哪个线程呢,就来调用这个需要被同步的代码。
24:03
啊,这个同步监视器呢,我们俗称呢,叫做锁啊。哎,俗称所,哎这呢就提到了哪个线程获取了所哪个线程。哎,就能执行这个需要被同步的代码。呃,那么其他县城没有货的这个锁,那其他县城就得等着。啊,其实这个有点像什么呢,就大家呢,去这个厕所里边是吧,哎,你一进去了这呢,是不是有一个门锁呀。哎,你把这个锁一锁,哎,相当于你这块呢,就理解成了你获得了这把锁了,然后呢,谁也进不来。啊,都得在这等着啊,你出去的时候呢,你把这个锁呢就打开了,你打开别人呢就可以进去了。啊,你要是不锁这个锁呢。都能进去是吧,那不就不安全了吗?那就这样啊好。这呢,就我们说的这个事儿啊,然后这个锁这块有没有要求呢。
25:00
哎,这块我们提一下啊,这个同步监视器。啊,这块我们提到说它呢,可以使用任何一个类的对象充当。哎,言外之意呢,就我们这个位置上放的其实是个对象哈,你别放基本书也行。哎,可以呢,使用任何一个类的对象充当,对于这个类呢,没有要求,但是。它唯独有一个要求,就是多个线程必须共用同一个同步加然器。啊,必须。共用同一个。啊,同步加然器。诶这呢说的非常明确哈,就是呢,对于这个是哪个类的对象呢,没有要求,但是大家必须得是同一个。哎,这个呢,就说完了。好,那这个说完以后呢,下边呢,咱们就来看一看怎么去修改啊好,那么回过来吧,咱们刚才呢讲的这个不安全,里边的这个呢,是使用这个实现的方式呢来写的。好,那我们首先呢,把它变成是一个线程安全的啊,然后我把这个代码呢,CTRLC一下,咱们就在这个able的safe里边啊,CTRLV我就粘过来了。
26:09
好就站到这儿了啊好把这些呢,我们就暂且都关一下,好打开这块呢,使用啊实现这的方式呢,实现卖票这呢,我们就哎同同时哈,哎使用。哎,使用呢,叫同步代码块啊解决,哎上述卖票中的。诶,现场安全问题。OK啊好这块的话呢,我们说一开始的这个写法呢,它是存在啊。在现场安全问题的。啊,这个咱们都知道了,好,那下边我们看该如何去解决啊,这里边儿这个重点呢,其实就是我们需要确定操作共享数据的代码。那首先呢,要明确谁是共享数据。显然呢,是不是就7K的了。那就它了啊,那么操作共享数据的代码是哪一块儿。
27:01
哎,准确来讲呢,是不是就这块儿了。对,但是这块你光包if不行,那是不是顺带着这个else也得包一下了,所以呢,就包这一块了。诶,那有没有同学会说说这块包住,那要是万一少了呢,我就多包一点得了。把while呢也给它包住。咱们先不包一会儿呢,看看包一下行不行啊,目前呢,我们看到的就是把这块包住是吧?好,这块包住的话呢,我们就这样来写了啊,首先在这块嗯,Sronized啊,其实你看这块SY这样一写这个单词就出来了,你可以来个回车就出来了。但是这个单词呢,你还是要自己会写啊。好,这个位置呢,我们整个大括号把这一段代码给它扔进去,诶我就放在这儿了,往后移一下,哎就可以了。然后这块呢,需要我们填一个对象哈。这个对象呢,是哪一个类的对象没有要求,但是呢,要求得是唯一的。对象目前好像也没啥对象是吧。
28:00
那我们可能就想着造一个吧。啊,就在这儿,比如造一个。造一个什么类的对象呢?他又说了,无所谓。那咱们就new object吧。哎,Object啊,哎obj哎这么着了,然后呢,这就是这个对象名,我把obj呢就往这一扔,哎从语法上没问题。接着的话呢,你要小心一点啊,这个obj啊,咱们说要想保证它的安全性,这个obj必须是唯一的啊。那我们看一看它唯一吗?什么叫唯一呢?就是我们在整个这个线程操作过程当中,有几个这个obj的对象啊。对,我们就造了一个Co TK的一个对象,然后把它呢,放到三个线程里了,然后这个对象就一个,其实这里边儿这个成员变量是不是也就这一个呀。哎,所以这个呢,其实是唯一的。啊是吧,啊是唯一的啊yes,那就是是他既然是唯一的了,哎,我们这块包的如果也没有问题,那这个事儿呢就成了。
29:03
来,我们右键做一个run。啊就结束了,结束以后呢,你回过去去看啊,前面这块呢,没有这种所谓的重票,这呢也没有这个错票,但是呢,有个问题全是窗口一的是吧。哎,不太对呀啊,怎么都是窗口一呢。哎,这个确实呢,是一个巧合。还还真的是他是吧。那就是这时候我们就担心是不是程序写的有问题是吧。其实这块的话呢,其实我们先理论上分析一下啊,我这个线声音呢,进来以后它呢会握的这个锁,它一握这个锁它就进去执行了是吧。他执行完以后的话呢,它会出到啊,出了我们这个大括号了,他一出这个大括号呢。哎,他这时候就会释放这个锁,他一释放的话呢,按说别的线程就会进来是吧。但别先生还真没进来。其实这块跟这个CPU呢有关系,就大家呢去执行,可能跟我的情况就不一样了啊,那为了去呈现,说别的县城是不是真的能抢,那我只能在这个位置呢,让你这个线程一窗口一你你你就别抢了,你稍微的睡一下得了。
30:10
咱们来一个五毫秒吧。不算多啊。哎,这么着。1.print啊好这块呢,你看我们再去做这个软执行,你看别的窗口是不是就能抢到了。哎,说明我们这个代码呢,没问题啊。诶这几个窗口交互着去取票,上边呢,诶没有重票这块呢,没有所谓的错票,诶这呢就是正确的,这个分析的话呢,清楚吧。哎,我这儿呢,也有相关的一个图,稍微的看一眼。哎,这么着啊,哎这呢,我们用这个C子呢,把操作TK的这个代码呢,给大家包起来了,现在呢,有三个窗口三个线程来卖票,诶这时候我们有一个概念,就这里边有个叫同步监视器,哎你可以想象成呢,这块一开是个绿灯一样。哎,一说到这儿呢,我们可以再假设一个场景,大家呢,火车应该都做过了啊,火车的话呢。
31:04
嗯,就是以前的那个火车,现在高铁好像稍微有点区别啊。绿皮也不一定非得是绿皮了,就原来这个一节一节车厢的时候呢,它在这个车厢的连接处呢,这块它上面有个灯是吧。现在写的叫有人。无人是吧,以前的时候呢,是有个灯啊,就是这个绿色呢,就表示这个厕所里边没有人。啊,没有人你就可以去,你要一进去的话呢,你把门一锁,这个灯就变成红的了是吧。诶其实这块呢,大家就可以想象成就是这样的一个场景一样啊,说现在有三个县城,就好比三个人他要去厕所了是吧,诶大家呢,就看谁能抢到这个,大家要抢到这个灯一样是吧?诶能抢到这个叫同步监视器了啊假如这个线程一呢抢到了,他呢就进去了,他一进去呢,他就把这个。门给锁住了。这不就变成个红色了吗?哎,他在里边呢,就一顿操作,他操作的过程当中呢,咱们不是有这个sleep的方法吗。哎,这时候我们发现哈,即使你sleep了,他也没有去释放这个锁啊。
32:00
哎,你也可以想象呢,就是你把这个你去厕所的时候,你把门一锁在里边给睡着了是吧。哎,睡着的时候呢,门也不会。自动开是吧,哎,还是安全的啊好,那我们这个线程一呢,在这里边呢,就一顿操作了,即使它阻塞了也没关系啊,因为这时候还锁着呢,这时候别的线程谁想进谁也进不来。哎,当呢,你执行完以后。哎,你出了这个C空这个大括号了,你把这个同步监视器呢,就给释放了。一释放,别的县生就能够进来了。当然了,有可能下一刻还是你。是吧,诶又被抢到的,这呢就是我们说的这样一个场景,这儿呢,就是这个同步间然器,它的一个意义所在啊。好,这个我们就说清楚了,那么回过来啊,这个位置呢,咱们写的是obj啊,写任何一个类的对象呢都可以啊,为了体现这个任意呢,咱就随便来一个,比如说呢,咱们整一个dog。哎,专门来看门的是吧,哎,这是一个dog,里边我啥也不写啊,然后回到这个位置呢,咱们就哎,Dog。Dog,哎,你一个dog是吧?
33:00
哎,这个就是一个,哎,一个狗啊,然后呢,这个位置啊,能能写狗不。哎,复制一份,对,我把这个呢注释一下啊,对这个呢,我就写成这个叫dog。哎,那这同样的问题啊,这个dog是唯一的吗?也是是吧,跟object其实一样啊好,所以呢,这时候我们去做这个run的时候呢,没有任何问题啊。啊,也是一个正确的一个场景。行,那说到这儿的话呢,我们可能就会想说,那能不能写一个。这还都得自己造是吧?有没有一个相对来说比较现成的一个对象呢?我们说呢,还真有这个时候呢,发现啊,最好用的当然了,不一定能用哈。现成的呢,就是这。因为这是非静态的方法。那这个调查的方法不就是对象吗?所以呢,这这次不就是代表对象吗?那同样的问题,这是唯一吗?这次唯唯一你得看这个,这是谁?
34:03
对,这叫当前对象是吧。哎,注意这个,这次呢,你可千万不要理解成这次是线程啊。thad.current thread,这才是线程是吧?三个线程不一样,这个呢?是不是就调了一个方法的对象,这个方法的对象是不是就他的对象。他的对象就造了一个吧。哎,所以说在我们这个题目当中,这次不就是S吗。S就一个,所以说它是唯一的吧。侍卫的啊,然后呢,呃,这个就是咱们这个题目中的。对象S啊。哎,所以它也是唯一的,那既然它是唯一的这个C呢就好使,所以我们去run的话呢,就不会出错是吧。所以最方便用的呢,我们就用这就可以了。行,这个说完了啊好这块呢,大家要注意它一定是唯一的,这个说完之后呢,然后呢,你再看一下这个事儿啊,刚才我们说包的时候呢,说诶要不要把这个well也给它包进去呢。
35:03
相当于呢,我们就这样了啊,我CTRLC一下,把这个呢先注释一下,把这个while呢给它包住,是不是就成这样了。CTRL的加改。那么这个我要是执行的话呢,它安全吗。啊,安全的话,然后呢,符合实际嘛,是吧。对,就不太符合了。你看我把这块收一下,这块收一下。这个呢,会出现什么情况。从这到这儿是吧。对,你比如说我们这个某一个县城呢,抢到这个锁了,它里边呢,什么时候会出来呢。就是当你把票卖完的时候才出来是吧,另外俩先生在这一顿等啊,然后这块出来之后呢,一进去发现呢,这都没啥事儿了是吧。哎,所以这个时候呢,你在这块再去加这个sleep,那也不好使,就只有这一个县城啊在买票。这个呢是一个错误的情况。就不合适啊,这你根本都不是多线程问题了,所以呢,诶这个呢,就不要把它写这儿了啊,这个呢把它注释一下。
36:06
呃,应该呢,是在这块呢来考虑。啊,这样才行。好,这个呢,就是我们说的关于第一种,哎处理的这种方式。诶,这种处理完以后的话呢,下边我们来考虑一下这个继承的方式当中,诶我们提到了也有现上安全问题,那该如何去处理呢?还得用同步代码块,还是呢,要注意诶到底应该包谁同步监视器呢?是不是是唯一的,唯一仍然不安全。那这儿呢,关于这个实现的方式的一个现程安全问题一个解决呢,咱们就说到这儿了,然后接下来看一下这个继承的方式当中的啊继承的话呢,咱们是写到这个里边了,然后把这个呢,我们CTRLC一下,诶放到咱们的这个位置啊,CTRLV,哎把它呢粘过来好,出来以后呢,这块我们稍微的调一下啊这个呢,不妨我们就叫做呃,Window,诶先改这吧。给咱们在这儿给它重命名一下啊。嗯,这个呢就window test我就叫叫它了啊,把这一就去一下,这个呢把这一也去一下,好这呢是我们写的这个继承的方式,这种方式的话呢,首先我们演示一下它的线程安全问题,哎,这呢会有这个问题了,接下来的话呢,我们看一下如何去处理这个问题了。
37:14
诶,这块我们在这儿写一下啊,说呢使用。同步代码块的方式解决现场安全问题。好了,然后这个呢,该如何去解决呢?诶这块我们首先呢,也定位共享数据呢,叫做T,然后呢,我们应该把操作共享数据的代码呢,使用C呢,给它包起来,自然而然的话呢,就也是这样的一段代码。好,这块包的话呢,你看ctrl out加个T是吧,哎这呢,我们其实就有一个sized是吧,哎,这个你点一下就直接呢给它包起来了,然后这个位置的话呢,我们就可以写个叫同步监视器,而且呢是必须要写的了。啊,那由我们前一个实现方式的一个经验来讲,这个位置呢,我们就会想到说,诶最方便的呢,似乎写一个Z就行。
38:00
当然,这时候我们就要想,这次呢,靠不靠谱儿?哎,靠的话呢,你就得想这个this代表的是谁是吧。在咱们这个问题当中,对当前对象了啊,就是调run方法的这个对象。哎,那也就是我们当前这个的对象。那window的对象呢,往下一看呢,123是不是三个呀。所以呢,我们此时呢,这个Z呢,就好比是哪个线人过来调的这个软方法,谁就是这个Z了,所以这个Z呢,此时。哎,表示的就是W1W2W3,那就意味着有三个同步监视器,每个线程一个,那显然呢就不靠谱了。啊,那就有点类似于什么呢,希望大家呢,在这个车厢里边,然后呢,本来呢应该是啊,每一个人呢,如果看成是一个县城,大家呢,应该只看同一个这个灯,这灯呢是绿的啊,你就进去啊,你一进去之后呢,然后呢,你把这个灯呢改成红的了,别人一看,哎红的就相当于大家呢都得共用同一个这个灯。但是现在话呢,成了啥呢。
39:01
对,三个窗口,三个线上,每个线上一个灯,就相当呢,在每个座椅上呢,你自己这块有个灯。然后这个人呢,进去了,然后他一关门一锁,把他自己这个灯呢,从绿的改成红灯,然后你自己想去厕所一看,你这个灯还是绿的。你就充进去了是吧。啊,这块就出事了是吧?诶所以这块的话呢,显然不靠谱啊,那么这块我写这次的情况下呢,你看我们再去做一个run。哎,你看发现它仍然会有这个重票,这呢叫错票了。显然呢不行啊,呃,这个这次呢不靠谱,可能我们就想,诶,那我们自己去一个。Object。啊,那么前面呢,我们就声明成是一个。Object了啊,那这时候这obj呢,放在这儿行不行呢。那现在写一下说,嗯,这个表示的是他们三个说不能保证。啊,不能保证呢,这个锁的唯一性。哎,这个呢是不可以的,然后把这个我们再复制一份,把这个呢主掉,这个呢改成obj是吧,那这时我们就要考虑obj是不是唯一的。
40:08
喂吗?仍然不唯一。对,因为呢,这时候它是一个实例变量嘛,实例变量的话,你造仨对象,这不还是有三个吗。哎,当然我们稍微处理一下就行了。是不是就可以了。对的啊,首先呢,我们不加这格呢,此时我们先跑一下。诶,你看这块仍然是有问题的啊,重票错票都出现了,然后呢,加上一个static。啊,那此时的话呢,我们这个表示的就是全局唯一的啊这呢不管有几个窗口,我们就只有这样的一个实例哈,好,我们再去做一个run。OK,那显然呢,此时呢,就是线程安全的了,所以这块OB街啊,这个我们说呢,使用诶死在这个修饰以后。哎,就能保证其啊唯一性。哎,这那就可以使用了。好,这呢是我们用的这个,呃,Obj这样一个场景,那能不能有一个更好的选择,就像我们刚才呢,在这个实现的方式当中,咱们就写了个Z啊,那显然呢是比较方便的啊,因为呢,我们本来就存在一个这个对象啊,就是Z次了,它呢是唯一的,但这个里边呢,不能用这次了,那有没有一个现成的,而且还是唯一的一个结构可以选择呢?
41:22
啊,这块呢,其实也有啊,这个是谁啊,诶我把它呢打开啊,这个呢,诶我写成是豌豆。加class。诶第2CLASS啊,这个呢,诶其实是有点超纲的啊,我们到后边讲到反射的时候呢,再去解决这个事儿呢,会更好一些,但是现在的话呢,咱们就先给大家抛出来了,那就用它呢是比较靠谱的。哎,这个位置的话呢,我们说呢,应该放一个对象哈,说你放的这个东西呢,是一个对象吗。我怎么感觉像是个类呢?哎,注意这块呢,其实也是一个对象,这个我们后边讲反射的时候呢,会有这样的一个结构哈。就相当于呢,我们声明的是一个window class。
42:03
它呢,就是一个值,谁的值啊,是我们这个大的它的一个值。哎,我这样来写啊。这么着,那么通过这样的一个格式,大家来看,这呢是对应的这个类型,这是类型的一个变量名,然后右边的就是它的这个值。它代表什么意思呢?就是咱们加载到内存当中的任何的一个类,这个类本身就充当了这个类的一个对象。这个类呢,不是别的,那长得就非常有特点,就叫做class。注意,这个class可不是咱们这个关键字class哈,我这是大写的C,相当于这就是个类。有个类就叫做。Class。他的对象呢,就是具体的某一个具体的类了。啊,有点懵是吧,这个我们后边讲反射的时候呢,再说大家现在呢,你先认可一下这个结构呢,还是一个对象哈。啊,这个对象呢,就是window class,那就相当于是我们加载到内存中的这个类,这个类的话呢,因为就加载一次嘛。
43:00
所以说这个类本身呢,它就是唯一的是吧。哎,这个我们说它是唯一的,那既然是唯一的,我们就能写它,所以我们去做一个run呢,就没问题。照样呢也是安全的啊,那可能有同学会说说,那我们回过来这个位置呢,是写的C在这写的哈,那这个呢,能不能改成说呢,我们叫这个类点class呢。可以不?当然可以了,因为它肯定也是唯一的,加载到内存这个类呢,我们都是唯一的。啊,只不过呢,这块呢,我们都不用去写它直接写个这很方便也可以是吧。那就行了啊。好,那么这样的话呢,咱们把这个同步代码块的方式啊,针对于呃,实现的方式,继承的方式的线程安全问题,我们就解决完了。哎,在这里边呢,我们稍微的啊,在这呢去呃说一下这个注意的事儿啊,怎么着注意呢,咱们在使用这个同步代码块的时候呢,必须要显示指明一个同步监视器啊,或者我们叫锁也行啊,那么这个锁的话呢,我们在指定的时候呢,可能会就会选一个比较方便的一个这个这个对象呢去使用了,那么我们通过刚才这样一个演示呢,会发现说呢,在实现的这种方式当中啊。
44:10
诶我们发现呢,就是使用这次的可能性比较大,我们就写这次呢没错,但是在技能的方式中写这次呢,好像呢就不太靠谱了,那我们改成了叫诶类点class的方式了。啊,这块我们这样说一下啊,在实现。呃,接口的这个。哎,方式中。啊,这个同步监视器。嗯,大家呢,可以考虑。使用这次。啊,可以考虑使用这次而呢,我们在这个继承。啊,这个类的这个方式中啊。继承类的这个方式中。这个呢,同步监视器。这个呢,大家呢,要慎用哈。
45:01
这注意,你看我这说的叫慎用,我可没有说不能用哈。还得具体问题具体分析,关键的你就看是不是唯一的是吧。只要是唯一的,那就能用。啊,这块呢,我就要慎用,后边呢,咱们有相关的练习,我在技能的方式里边,我就用这个。你看为啥就行啊,还是从这来看啊,慎用,这次那么可以考虑。哎,使用,哎这个我们说当前类是吧。啊点class啊用它呢去替换。啊,是比较靠谱的。行这呢,就我们说的这样的一个结论啊,这块呢,大家注意一下就行了。
我来说两句