00:00
好,那么今天的话呢,咱们继续来讲县城啊,县城内容呢,咱们就能截了,呃,相对来说呢,县城这块呢,我们重点的问题呢,是比较明确的哈,第一个的话呢,就是如何去创建多线程,那我们诶比较经典的是讲了两种方式,继承RI类实现renoable接口,那对应的就是我们这个IQ501里边的这两种方式的话呢,大家一定要把它记清楚,那么到最后的话呢,咱们再讲两种方式。一个呢叫实现接口,一个呢叫使用线程池,这个呢是在GDK5.0新增的这个新特性。啊,这块呢,我们相当一共是有四种方式啊,后边两种的话呢,咱们作为一个了解,作为一个补充给大家呢引进来,呃,这呢是我们说的第一个重点,然后第二个重点的话呢,就是这个我们说解决县城的安全问题啊,这块我们就提到了这个同步机制啊,OK,就是我们这个零四里边呢提到的这个内容。好,那么这个中间的话呢,我们又讲了讲这个thread这个类呢,它的常用的方法包括现成的生命周期,生命周期这块呢,我们主要提到的点是什么呢?就是哪些方法的调用。
01:05
会导致这个线程呢,从一种状态切换到另外一种状态,这个呢还是有必要我们来关注一下的。OK啊,就是我们说的这样一个过程,然后呢,生命周期这块呢,我们提到的是1.5之前和1.5之后,呃,两种不同的这个状态,主要呢,针对的就是这个,咱们这里写的这个阻塞呢,没有具体去细分了啊,这个细分的话呢,还记得我们分成几种状态不?三种是吧,哪三种啊。啊,一个呢叫做blocked是吧,哎,另外一个呢,叫做waiting,还有一个呢。Time waiting对,记着这个事儿,然后呢,这两个呢,合成一个了,叫做reable是吧,就是可运行的一种状态,所以说呢,你是正在运行呢,你还是现在呃,没有运行,准备着去运行啊,其实呢,主要区就是C有没有去执行,但是都是属于一种可运是吧,或叫都可以啊。行,然后这三种状态的话呢,其实我们也简单提了一下,像这个微信的话呢,比如说我们失去同步锁,这呢,就算是你处于一种block的状态。
02:08
然后waiting的话呢,那对应的比如说我们这个we的方法,你这块呢,没有带时间的这种,这都属于waiting啊time的waiting呢,就是凡是带时间的这种。啊,这个waiting的话呢,Wait的话呢,它不是有时间吗?这个带时间的这种都属于有时间限制的这种等待啊OK,行,就这样个情况,呃,然后的话呢,咱们接着呢,这个县城的这个同步机制呢,往下来说,这儿呢,有一个艾特硅谷零四啊,关于县城安全之后的几个问题。这块的话我们要讲三个问题,第一个话呢,就是有了同步机制以后,我们就可以解决前面提到单利模式当中的懒汉式的线程的安全问题了。这呢是第一个点,然后第二点的话呢,线程的同步机制呢,哎,我们说呢有好处。诶解决了线程的安全问题,当然了这个弊端的话呢,相对应来讲,在同步的这个过程当中,多个线程只能是有一个线程在执行,这个呢,其实我们相当于呢叫串行了。
03:04
在这个过程当中。所以性能的话呢,稍微的差一些。但是呢,还是优先考虑数据的安全性啊,这是最重要的,那么还有一个问题呢,就是我们对应的带来的可能会造成磁锁的问题。这个死锁的问题呢,我们需要呢去关注一下,包括呢,什么原因诱发的死锁,如何呢去避免死锁,这个呢在笔试面试当中也经常被问到的。哎,这样的问题好,这呢是说的第二点,然后第三个点的话呢,是5.0的一个新特性。诶,大家会发现呢,我们在5.0当中啊,讲过很多的新特性,这个呢,也是Java历史上呢,最重要的一个版本啊,成为里程碑式的版本,那其次呢,第二个里程碑式的版本,比它稍微的差一点的就是8.0了。哎,就是8.0OK。好,那么在5.0的时候呢,提到了一个接口叫做lock,翻译过来呢就是锁,然后这个呢,是区别于同步机制之外的另外一种解决线程安全问题的方式,诶我们把这种方式呢,也给大家呢补充的讲一讲,这个呢,就是我们这个零四里边补充的三个内容,那首先来看第一个。
04:10
那么单利模式里边我们提到过鄂汉市和懒汉市,显然呢,鄂汉市是不存在线程安全问题的。男士呢,存在。啊,那么我们这呢,把这个懒函式呢,先复现一下,然后说一下它的这个线程的安全问题,OK好,那这这呢,我们就在这个singleton是吧,在这儿右建啊,我们去新建一个,这呢不妨我就叫做这个bank的一个测试了。好过来啊,这个呢,我们是。嗯,实现线程安全的。啊,懒汉式OK。好,这呢,我们把这个问题呢,首先呢给大家浮现一下,哎,这个呢,我们就还拿这个办法来说事了啊,哎,回忆一下我们如何呢,去这个写一个懒汉式了啊。首先呢,私有化这个构造器是吧。这呢,我们就不单独的提供这个对象的这些实例变量了啊,这就不写,比如说银行名啊啊等等是吧,这个情况了啊直接呢,我们就这个考虑它里边最核心的这个逻辑了,扩大器,然后呢,内部呢给他造好是吧。
05:10
这然我就直接。Public public private。嗯,斯代克。Bank。嗯,叫instance吧。然后这块我们先不给他造好。然后呢,通过一个方法给他去返回。嗯,Sta的返回一个bank叫get吧。哎,在这里边判断一下是吧,如果呢,这个incense呢,是。然后呢,我们这个ins呢,就给它new一个bank。然后在这个位置呢,我们去return一下这个instance。哎,这么着。没问题是吧。好,那我们所说的这个线上安全问题,当时是怎么描述来着?对,相当于呢,是有两个线程,至少得俩线程了,去调这个方法,调这个方法的时候呢,相当于呢,他们主要的目的呢,是要获取这个incens这个实例了,那么这个实例呢,其实就相当于我们的共享数据。
06:09
那第一个线程呢,判断你是no吗?一判断是no它就进去了,A进去这呢,假设给sleep了啊给阻塞了,它一阻塞的话呢,实际上并没有创建成功,那么这个时候呢,第二个线程就也进来了。他进来化呢,是不是也给创建了。啊,那么这时候呢,就导致我们这个策呢,被先后执行了两次啊,那这时候呢,就显然就不对了。啊,那么这个问题我们能不能给他描述出来,演示出来呢?可以,哎,那我们首先来做这个事儿。好这呢,我写了一个没方法啊,然后呢,在这个上面呢,我们现在这么着一下啊,Bank,我叫BE1吧,它先是个no啊。这个B2它也是个no。然后呢,为了在这个没方法里边去调,我就不写成通过对象来调了,咱就直接把它静态一下啊。这样的话呢,我就直接可以在这个酶方法里边呢去调用了,呃,我这儿呢,去提供两个线程,每个线程呢都去调这个方法。
07:07
OK,先整第一个啊。New一个thread,咱就整这个匿名的子类的对象了。哎,所以这块呢,我们前边先声明成是thread。要提一了。然后在这个位置我们整一个大括号,把这个方法呢,给它重写一下,对吧。哎,那么这里边儿我们做什么事呢?这里边儿我们要做的事呢,就是通过我们这个bank呢,去调这个盖森这个方法。方法呢,返回了一个对象,我付给B。然后呢,这呢是我们的这个T1是吧,哎,我让他呢去调个四大方法,这是第一个线程。啊,那我们再提供一个啊,这呢笔我就写这儿吧。XT2,这个呢,我们让他给了B2。哎,先后呢,让他俩呢,都。Start一下是吧。这个没问题吧。好,那么他俩都打完以后。嗯,他们先后的去调用我们这个方法,然后都返回的这个incense,哎,这个incens的话呢,其实就是个地址值,把这个地址值呢,一次给了B1和B2。
08:08
那么有可能是这个调的时候呢,返回的一个,然后这个调的时候呢,它又重新又扭了一下,所以呢,这俩地址呢,就有可能不一样,那咱们看一看,他俩地址一样不一样。这个一个B。一个B2,顺带呢,我们直接打印一下B一等等于B2。按正常来讲的话呢,应该得是相等的,否则的话,你这就不是单立了。啊,但事实情况呢,有可能不等。啊,那我们为了让这个问题呢突出一点,我们在这个位置呢,可以给他加一个是吧。比如加上100毫秒,让它显示的在这儿睡一会儿。这样的话就更容易呢,出现我们这个。线上的安全问题了啊,但目前这个写法呢,有没有问题呢?其实还是有点问题,你看我们run一下。哎,当然你会发现,诶,怎么是闹呢?因为都是闹,所以都是处是吧。怎么没获取到呢?
09:01
哎,这个大家呢,昨天啊,这个课间的时候有同学问我类似的问题哈,这块要注意一下,就是我们这两个线程跑出去之后呢,你注意这是两个分线程出去了。然后我们这个主线程是不是还在执行。呃,目前之所以是闹,就是因为。主线程先执行了。他执行的时候呢,你这两个还没有去调那个get方法呢,所以那不就闹了吗,怎么办。对,有同学呢,可能想到第一个方式,我在这块睡一会儿是吧。就睡一会儿呢,就是给这个时间让他俩去执行。啊,但是你睡这会儿呢,也不一定靠谱。是吧,最靠谱的话呢,对给大家说的这个照按操作嘛。对T一点照啊,T2点照按一下就是保证了你这两个线程呢,让他俩参与进来,都执行完以后,我们这个主线程才继续向后执行是吧。哎,分别呢,我们就给大家拆开一下。那alt加回车。诶就这么着好,那么他俩都执行完以后呢,我们这个主线上才往下走,这时候呢,它它就有地址了,我们再去做这个run。
10:03
好大家看啊,这个时候呢,我们看到的,诶这里还是个处是吧。这就相当于呢,他俩这个地址呢是一样的,那挺好,我们希望看到的是这个情况又来一遍,还一样再来一遍。还一样,那把这个地把这个改一改吧,1000。哎,再来一个是。不一样了是吧。哎,有区别了。啊,再一样呢,还紧张了是吧?诶不一样了,那这时候我们就看到呢,他俩拿到的不是同一个地址啊这呢就是我们说的叫现成的不安全性单利嘛,你这怎么会你有两个呢,显然呢不合适了,哎,那我们要解决这个线程的安全问题,那主要的操作的位置就是针对于我们这个方法了。那我们现在要针对这个方法呢,来考虑怎么去处理。嗯,首先的话呢,我们看到呢,这里边儿的共享数据呢,就是incense。那针对于他呢,我们去做一个处理发现呢,诶操作CES这个代码呢,完全的就放在这个方法里了。
11:01
那是不是可以考虑把这个方法生命成是一个同步的呀?诶,所以呢,这就是一个同步方法了,哎,就这样来写,那同步方法呢,大家注意它的同步接然器呢,这个你改不了,它就默认的了。那么此时这个方法呢,是一个静态方法。它的同步显然器。对当前类本身是吧。这个呢,我们说默认为就是咱们这个点。诶就它好,咱们昨天也都提到了,后边讲反射的时候呢,会说这呢其实也是个对象,所以呢,同步加器呢,一定是由对象来充当的,那么这个类呢,加载它在内存中呢,会缓存起来就加载一次,所以说它是唯一的,进而呢它是线程安全的。所以呢,我们再去做一个run。那此时的话呢,不管你这块睡多久,他那得到的地址一定都是一样的啊,你再测也是一样。那那都没问题。
12:00
行,所以呢,在多先生这块,你看有时候我会多测几次哈,包括刚才我们这个是100毫秒的时候呢,测了几次呢,发现地址都是一样的,就是这儿呢,其实就是我们之前说到的最恶心的一种状态。诶什么意思呢,就是这个代码执行的时候呢,你发现你执行了三次五次都是正确的,但是呢,其实它有错误的可能性。啊,就是。这个不明显是吧,出现的概率比较低,所以呢,你没有发现这个问题。啊,这呢,就需要这个经验是吧,你得知道这里边儿会存在相关的问题,包括呢后边这个思索。呃,死锁的话也不是说每次都出现,但是呢,它是有出现死锁的可能,这个你要提前呢,得预防这样的问题,要不呢,程序员一旦程序一旦构成死锁以后呢,这不就相当于进入一个阻塞的状态了,始终还解不了。那这个呢,是不正常的状态啊。好,那么这儿的话呢,我们就解决了这样的一个问题了,我们可以把这个呢看成是啊,这叫线程安全的第一种方式。啊的方式一啊,既然我写个方式一了,那就可以考虑呢,还有其他的方式。
13:06
好,咱们把它呢,CTRLC一下,然后呢注释一下。哎,这个我再粘过来。嗯,这个我们写一下啊。叫实现线程安全的方式二。哎,方式二这个方式二的话呢,其实跟方式一啊差不太多。嗯,我把这个呢呢给它去了。嗯,我们呢,刚才呢,是把这个方法呢,说明是一个同步方法了,那其实呢,我们也可以主动的去使用一个同步代码块是吧。那无外乎呢,就是把这个代码呢,给大家包起来呗。啊包起来,然后呢,Ctrl alt加一个T。这个呢,找一下S的,哎,在这个位置,那这个位置的话呢,我们这个同步监视器主动去写一个,记着要保证唯一性,写谁呢。对,能写这次吗?你这在静态方法里边不能出现这次是吧,诶所以不行了,最方便的呢,是不是还就是我们刚才提到那个默认的是吧。
14:05
就他吧。哎,对,就写这个,这呢是最方便的,最合适的。这个其实跟我们上面这个没有本质区别。只是呢,咱们主动的在这里边把incense相关的代码呢给大家包了一下啊。嗯,其实这个呢,稍微有个小细节呢,就是这个return这个操作呢,我说呀,其实你放到外边呢,能稍微的。好一点。啊,或者好一点,其实也没什么区别。那这礼拜都行啊,诶你看现在的话呢,如果我们有一个线程,两个线程进来了,这第一个线程呢,假设呢,他拿到这个同步监然器了,他就开始进去执行,然后这时候你第二个现场就在这等着。那第一个呢,出去以后呢,只不过呢,在这儿稍微的,呃,我们说呢,释放这个同步监视器呢,可以早这么一步啊,释放以后它还没有return呢,第二个可能它就进来了。呃,但是这块呢,这个因为你也没有去修改的instance,所以呢,对于我们第一个线上来讲,他可能在这块呢。
15:00
释放了同步监视器以后呢,它在这儿假设也进入阻塞状态了,那第二个线程呢,进去以后呢,其实也不影响假设第二个出来以后呢,有可能第二个线程先拿到这个森走的第一个呢后拿的,其实这块呢,因为没有去修改inces。所以我放在外边是不是也行?体会一下啊,就是这个操作,你放到这儿也行,放到这儿也可以。好,这个呢,也算是咱们的叫第二种方式了,这个呢使用的是同步代码块,然后基于这种方式呢,我再稍微的做一个优化。CTRLC注释一下。哎,我在这呢,整一个叫三。好,这块的话呢,大家稍微注意一下了。诶大家你看哈,目前的话呢,我们假设两个线程过来了,第一个线程的话呢,它这块呢,去握着这个同步监视器,它就进去了,然后呢去操作,然后第二个线程,乃至于呢,后边还有其他的一些线程都得在这儿等着。只有呢,第一个线程这块,它出来以后,释放了同步监视器了,其他的线程才有机会呢,进入到这个同步代码块当中。
16:03
这个也是我们说的这个同步的一种机制哈,那么这里边儿呢,去想个问题哈,如果呢,我们第一个线程它这块呢,已经出来了,也释放了,其实呢,由于你第一个线程进去了,我们这个ince其实就会被创建对象了。对吧,呃,那么创建以后呢,你想想,对于第二个也好,第三个也好,以及后续的这些县城。他们也不需要再去拗了。换句话说的话呢,其实这个ince呢就定死了,你就用就行。也就是说呢,我们后续啊,其实没有必要非得让这些县城都一个个去进这个同步代码块了,因为进去之后呢,你判断也没有。成功是吧,不就马上就出来了吗。那就希望呢,你后续这个线程呢,就是你再去调这个方法的时候呢,就直接拿着现成的这个ins走就可以了,没必要呢,再在这排着队呢,依次进去,进去呢你发现你啥也没干。因为判断是不是闹的时候不也没执行吗?啥也没干就出来了,当然还得一个一个进去,没必要。
17:01
哎,我说一下,在我们第二个方式的基础之上呢,稍微的再优化一下,我这样呢做了一个处理。再次判断它是不是nor。哎,如果是闹,我们就这么着呢,去给他处理了。我又外边套了一层。诶同学说,那这样这样套一层这个呢,是不是就可以不要了呢。就是这块这个衣服就不要了,不行。不行,那不又不安全了吗?嗯,不会吗?会的,比如这块,嗯,就就就这。假设呢,就把这个给注释了。相当于我现在只气功站呢,只包了个他,其实你想想气功站你光包括它,它就一行语句,这有啥意义呢。咱们原来说的说把操作共享数据的代码给它,这个用C包起来,你最起码不得包两吗。才保证是这个跟这个呢,保证在一个线上执行完以后呢,才行做一个整体,你现在就包了一个,这一个跟不包不也没啥区别吗。
18:03
是吧,从这个理论上来讲也能清楚,如果呢,要这样写的话呢,我们说这个仍然不安全,看能理解不。你第一个现场。判断是不是no进去了。这时候呢,阻塞了第二线程是不是也进去了。这不现在又先后的给他拗了吗?所以这样不行是吧,这个事儿呢,还是要打开的。只不过呢,在现有的衣服的基础之上呢,我又包了一个衣服,你看这个呢,意义所在啊。现在的话呢,我们一个线程过来了,这个线程呢,说你是不是闹是闹。是不就进去了。然后第二天的话呢,可能里边还没执行呢,第二天呢也进来是不是闹是闹是不是也进去了。行,我就以俩县城为例,这两个县城呢,现在已经进入了这个if了,然后西空站这块呢,只能有一个县城进来,这块呢,假设第一个县城进来了,他这块呢,把这个音森呢给他扭好了,扭好以后呢,在这块就出去了,出去以后呢,至于说他是马上就return呢,还是等一会儿呢,这都无所谓了。然后第二个线程的话呢,你再进去的时候呢,由于我第一个线程呢,释放同步监视器之前我已经扭好了。
19:06
所以第二线人进来的时候呢,这个衣服肯定进不去。哎,所以说你就绕过它直接呢,这块呢,就出来了是吧。所以呢,这个时候呢,第一个线程,第二线程他们拿到的都是同一个incense没问题,那么第三个线程这时候比如说再进来的时候。由于呢,我们第一个线上已经拗好了。所以第三个线程在进来的时候呢,你判断if的时候。已经进不去了是吧,所以没有必要呢,再去等这个同步监视器了,你直接呢,是不是就绕过去,拿着这个音寸就出去了。以至于后边的这些先生都如此。也就是说后续的线程只要呢,在我们的ins上被实例化以后呢,就不用再去进这个同步代码块了。是不是相较于我们这个方式二来讲会更效率高一些是吧?哎,那相当于方式一的话呢,自然而然效率也高。啊,哎,相较于。方式一和方式二来讲。
20:02
哎,我们说呢,这个效率。哎,更高一些啊,应该能理解这个事儿。啊,这个呢,我举一个生活中的例子啊,就好比是这个事儿。啊,现在呢,假设呢,某一个商品呢,极其紧俏。啊,目前呢,我们说到这种紧俏的商品。目前比较少了是吧。嗯,举一个什么例子呢,这个我一般呢都会举。啊,当年这个苹果的这个场景哈。哎,苹果呢,这个之前就是现在已经十三十四了是吧,这个比较早的时候呢,有个5S这样一款手机。啊对,很早了,对这款手机的话呢,在5S这个版本当中出了一个新的颜色,人家叫香槟金,咱们国内呢,就是叫土豪金是吧。对,这个颜色的话呢,极其紧俏。啊,当然我在北京的时候呢,还当时预定了这样一款手机啊,当然我不是住回龙观嘛,回龙观有个那个电信营业厅,诶我就预定了一款,就刚开完发布会,他这个机子呢,其实还没有他这个开售呢,还有段时间就在那,我就预定了一个香槟金。
21:02
啊,然后呢,我前面预定的人也非常多,号称呢说有一两千人都预测预定这个香槟金了,然后但是呢,我不着急用啊,其他的人他就比较着急嘛,然后他们就提前呢,就可能那个黑色的到了他们就买了,然后我就一直等,等到11的时候呢,告诉我有了,然后呢,号称呢我那台机子是回龙观地区的第一台香槟金,就那种感觉呢,其实自己没觉得怎么着,但是呢,跟别人一对比的时候呢,这种感觉就来了,是吧。啊,所以有的时候人这种虚荣心的完全不是说自己体现的就是买一个名贵的包啊,假设也没有别人,就你自己你也没觉得它有多贵。啊,当你跟别人在一起的时候,你发现这个价值出来了是吧,嗯,装一下是吧。好,那假如呢,你看现在这个店里边儿呢,就只有一台这个香槟金的这个手机哈,现在呢,一堆人都在这排着队,目的呢就是都想买这一台手机。但是呢,别人不知道说就只有一台。假设呢,这个就只有这一台,类似于我们这里边儿的这个new的这个行为呢,就这一次啊,那第一个人呢就进来了,进来以后的话呢,他就把这台手机呢给买走了,买走以后呢,他从那个后门呢就出去了。
22:07
出去以后的话呢,其实你想想,对于第二个人,乃至于说后边呢,排了1000个人哈。这些人的话呢,其实没有必要再在这儿等着了。啊,按常理来讲呢,此时呢,你这块立个牌就行啊,就写俩字告罄是吧,然后呢,大家就散了就得了。啊,就都走啊,但是呢,你要是没有这个牌的话呢,你让后边这个人呢,积极兴奋的每一个人都进来,排了大半天的队啊,进来之后呢,你告诉他说没有了,他才出去,第三个人再进来,没有了出去。那这个性能就太低了。那么这里边儿放的那个牌呢,不就类似于我们这里边儿的这个行为吗。说你就不要等了啊,这块你判断一下,我这已经不是no了,你们直接拿着现成的这个ins扫就行了。啊,就类似于这样的一个行为啊,大家呢,去对比着体会一下啊。行,诶当时呢,这个机子买的时候呢,印象很深刻啊,诶有两个事儿印象很深刻,这个画面至今忘不了,第一个画面呢,就是当时在这个营业厅呢,办这个手续的时候呢,旁边有个哥们就过来说,诶,你这是香槟金吗?我说是的是吧,他说能让我摸一下吗?啊,然后我特别淡淡说可以是吧。
23:12
啊,就是虚荣心爆棚是吧,然后另外一个事件呢,就是我拿这个手机,因为没过俩月就过年了嘛,我就回石家庄了,老家石家庄嘛,当时石家庄呢,火车站相对来说还比较传统,比较乱一些是吧。对,然后呢,就在得瑟是吧,等公交车的时候就在那儿拿着看,然后呢,上车的那一瞬间就是明显知道有人在挤我,上了车以后手机没了,对,就被偷了啊对,当时呢,石家庄火车站还是比较乱的。对。对,好早以前呢,不是有,是昆明还是哪出现过火车站这个杀人事件是吧。对,当然有人就调侃说石家庄的肯定不会出现这样的问题,因为呢,如果说要呃行凶的话呢,所以刀呢,是吧,已经被偷了是吧。啊,说这种事儿呢,也不会在北京发生,因为北京的话呢,这个刀都抽不出来是吧,人太多了哈。
24:04
嗯,OK啊,行。诶,所以说还是低调点哈,然后其实还有一个画面很深刻,就是过年的时候呢,也没买新手机,家里边儿有一个备用的黑黑白屏的这个诺基亚是吧。然后呢,我在回北京的路上呢,你发现就极其无聊。别人呢,都在看那个手机刷视频啊干啥的啊,我自己呢,看了看没有什么新信息啊,我就再放回去了是吧?诶非智能机你发现真的没啥,除了打电话发个短信好像也没啥别的事可干了是吧,OK。所以说呢,现在呢,已经回不去了哈,包括呢,有了这个智能机以后呢,这不因为老充电嘛,所以就会说诶是不是我要备用一下,万一特殊的什么场景。这个没电了怎么办?我再买一个功能机,你发现买了功能机以后真的用不上。真的就在那儿放着是吧。啊,因为呢,你这个现在的咱们的各种需求呢,功能机根本都搞不定是吧,OK啊。好,这个呢,就咱们说的这个方式三。啊,那我们要写的话呢,是不是优先考虑就写方式三了呀。
25:03
啊,对的,但是。啊,注意再说一个问题,这个方式三的话呢,还有点儿问题。但是这个问题的话呢,超出了大家目前的这样的一个知识的。呃,内容了啊,超纲了,什么内容呢,这里边儿呢,会存在一个问题哈,我们把这个问题呢称为呢叫指令重排。啊,这一说呢,就比较比较高级了哈,呃,我简单来说一下啊,就是在我们这个位置来讲。我们现在ins森的话呢,需要呢,给大家去new个对象,实际上呢,我们new对象呢,你看似呢,只有这一行代码,但是这里边儿呢,需要做很多的事儿。需要做很多的事儿,嗯,简单的让大家看一眼啊。啊,因为你要看多了,你会觉得说有点复杂。这个呢,是讲GM这个课的时候呢,其中的一章里边呢,我讲这个问题了。那我这儿呢,有一个叫创建对象的步骤啊,上面叫创建对象的方式,以后呢,咱们多多少少都会见到很多种方式,以前咱们最初的就是new啊,其实还有好多方式啊,这个先不管了。
26:04
就就就当然没有是吧,然后创建对象的这个步骤,你发现呢,这块也有好多的这个步骤。啊,然后最后这一块呢,执行in内的方法进行初始化,但in力方法呢,以前我们稍微的提到过,其实呢,它为代表的是不是就涉及到构造器啊。也就是说呢,我们构造器这块呢,其实是我们在这么多步骤当中的最后一步才开始执行的,那就意味着之前呢,它其实会做好多的这个事儿的,那就是说这一行代码呢,其实会分解成好多行代码去执行的。那么在执行的时候呢,会出现什么样的场景呢?就是我们其实一个线程进来了,判断它是不是闹呢,是脑。然后呢,这时候呢,是不是就进去了。进去的时候呢,注意一下,就是此时的话呢,我们这个对象呢,它创建了。对象创建了,也就是说这个in instant已经不是no了,但是呢,它有可能还没有执行构造器为代表的in方法。也就让这个对象有了,但是他还没有初始化完成。
27:01
没有真正的执行完它的所有的步骤,然后呢,呃,这个时候的话呢,其实我们的这种因为它有指令冲排哈,就是相当于这块没有完全执行完的时候呢,它可能这个呢,就有出去的可能,这要出去的话呢,你像你呃或者出去或者没出去啊,这时候呢,他还没有完全执行完因内的方法的时候呢,如果第二个线程进来了。你想这个第二个阶段要进来的时候呢,判断是不是闹的时候。已经不是闹了。你看这个new的时候呢,我们不是分成了,就是整个这个造对象分成好多步嘛,其中有一个环节它已经给创建了,也就是他已经不是闹了,但是还没有初始化完成。然后呢,你第二个线程要进来的时候问是不是闹呢。已经不是闹了。对吧,那这个时候呢,他就拿着你现成因森是不是就出去了。但是此时的CE呢?其实还没有初始化完成。对,就会有这样的风险。啊,那么怎么办呢?如何去解决这个问题呢?啊,其实解决的话案呢也比较直接,这呢就是以后在GC当中会讲到的这个啊,指定重排的问题,解决方案呢,就是我们将啊当前这个共享的数据呢,给它加一个关键字就可以了。
28:09
啊,这个我在这儿看啊,往上拽一下。嗯,在这儿啊,加一个什么关键字呢,这个叫volatile。看这写的从GDR开始分配空间,初始化调用过道器,是不是就我们说对象的创建的这个过程嘛。啊,然后呢,它会在线程的工作存储区呢,一次性完成,然后复制到主存储区,但是呢,需要我这样一个关键字来修饰,避免指令重排。啊,大家呢,可能觉得有点迷糊,我就不深入的去讲了,这讲的话这就比较多了啊,那么我们只要做一个事儿,就是你把这个incene呢,前面这块呢,加一个关键字,这个呢,就叫做what。诶加上它,加上它以后的话呢,它就能够避免这个指令重排啊,就会避免刚才说的这个问题。啊,这个呢,就是这个方式三啊,我在这稍微说一下啊,说为了避免出现指令。
29:04
重排需要呢,将咱们的。啊,然后呢,声明为。哎,声明为啊,这个我们叫,哎,我tell加上一个他就可以了。哎,所以这个呢,是从我们最终大家实现的方式来讲的话呢,你可以这样的去写。那就没问题了。好,所以说关于关于这个单利模式这块呢,其实有很多内容可以去讲的,包括呢,我们后边还提到过枚举类的方式。其实呢,还可以使用内部类的方式,诶都可以呢去实现,那对应的话呢,大家有兴趣你可以看看我这课件里边提供了一个使用内部类的方式。啊,也是OK的行,那么这儿呢,我们就把这个懒汉式写了三种方式,第三种稍微大家关注一下。那就可以了。好,这个呢我们就过了,然后接着的话呢,我们看第二个问题说呢,基于同步机制,我们说呢,可能会出现一个问题,那就是死锁的问题。
30:02
啊死锁死就是带呃锁就lock,所以就是带lock这样的问题。好,那么这块呢,我们就首先呢说明一下,怎么叫死锁呢。哎,先看这里边一个说明,说呢,不同的线程分别占用对方需要的同步资源,不放弃。都在等待对方放弃自己需要的土木资源,就形成了现成的磁索。哎,我这儿画了个图,比如我们这个线程一,它呢,目前呢,握着一个同步天然器。就我们说的这个锁了啊,他握着这个锁一,现在呢,在等这个锁二。那另外一个线程呢,先握着锁二,再等这个锁一。大家互相僵持着,你也不给我,我也不给你。咱们就在这儿等着。啊,那么这儿呢,就构成了四所。这个比较好理解是吧。就好比呢,现在呢,这是一个人,这是一个人现在要吃饭,很遗憾,只有一双筷子。而且假设呢,这个吃饭就都得用筷子哈,我用手呢,那不算哈,哎,但是呢,这个人呢,拿了一根筷子,这个人呢,拿了另外一根筷子。
31:08
然后都在等待对方呢,先把这一根筷子给自己。然后呢,这块呢,互相僵持着,最后呢,谁也别吃了。啊,就成这样一个局面了,就类似于我们这里边儿这个思索一样。然后呢,这边还有一个小故事是吧。呃,面试官问说你能解释清楚什么是死索,我就录取你,这呢,你可以理解成这是一个OBJECT1,这呢叫OBJECT2是吧,那你录取我了,我就告诉你什么是死锁,就拿着OBJECT2等着OBJECT1。然后呢,恭喜你通过面试了是吧。啊,这个呢,就是理解成是一个死锁的这样一个场景啊,嗯,稍微理解一下,好,这呢是死锁啦,然后呢,哎,先回过来。这是第一个事儿啊。那么第二次的话呢,如何看待这个死锁,就是说呃,我们写程序的时候呢,其实要注意是避免出现死锁的。啊,也就是说我们接下来写这个代码的话呢,大家呢,你看看就行,不要老练了,练多了一写一个思索也不合适是吧。
32:06
啊,我们编写。程序时。哎,要避免哎出现这个思索啊。好,那么这块呢,我们就给大家举个例子啊,演示一下这个思索的问题,这呢,其实我写现场的一个啊,一会儿我们再去看,我们先再来一个。Deadlock它的一个测试啊。好,没方法。诶,进来啊。OK,然后这块的话呢,为了演示方便呢,我用两个对象啊,都是叫string。嗯,Build这个类型的吧,这个呢,就是我们后边要讲的这个字符串相关的这个类啊,嗯,SSE吧。然后s be总感觉怪怪的啊。造两个对象。好造好了,造好以后呢,我在这块呢去呃,两个线程。哎,直接呢,我就调他这个start得了。
33:01
然后呢,在这个位置呢,整一个大括号啊。嗯,这里边做什么事呢?这个线程的话呢,一进来它要调这个run方法,首先呢,我这来一个S的,把这个S1呢当做这个叫同步器啊,也就说这个锁在这里边呢,我让S1呢去调。这个方法相当于就是一个添加的一个方法,添加一个A。然后呢,我让这个S2呢,去喷添加一个一。接下来的话呢,我们再去size一下啊,S2呢当做这个锁,接着呢让S1呢去添加一个B。啊,S2S1添加个BS2呢,添加一个二。添加完以后呢,在这个位置我们去打印一下这个S1和S2。哎,就这样个场景,那么在这个线程呢,沃德S1执行完这个操作之后呢,这个位置呢,稍微的睡一下。便于呢,我们去演示这个思索的问题。比如说100毫秒。
34:00
哎,就这么着啊好这个呢相对清楚是吧。把这个看一下啊,行,这个过程清楚吧。就是第一个线程这块过来以后呢,他先握着这个锁,然后呢,这个执行完以后呢,他稍微睡一下,醒了之后呢,他再握着另外一个锁执行这个操作,执行完以后的话呢,他就打印一下S1和S2。然后呢,我们现在呢,再来一个线程。哎,CTRLC。在下边,那么这个线程呢,跟它反过来,它先握S2,再握S1。然后这个位置呢,咱们给它改成比如说C这个呢叫D。这个呢叫三,这个呢叫四。这样一个场景,好,呃,假设呢,你要是没有这个sleep的这个时候。诶把它呢,我们诶关掉啊没有sleep,这个时候呢,我们这时候呢,做一个run。哎,你会发现呢,是不是也执行成功了,你可能再次执行呢,还是成功的。执行了100次,有可能都成功。
35:01
这呢,就我们说的,你每次都成功,不意味着这个程序呢没有问题。其实呢,它是存在有问题的可能性的。啊,那首先呢,看一下它这个结果我们能不能理解啊。这个从这个结果上来看的话呢,它应该是先执行的我们第一个线程。这个线程的话呢,在执行的时候呢,嗯,其实一开始的大家就可能这块不熟啊,你就先看成是字符串就可以了,这个字符串里边没有任何的数据,它呢就是先添加了一个A,就好比呢,这个字符串呢,就在原有的相当于是一个空的一个字符串的基础上呢,加了一个A,你可以就这样去理解就行。所以说呢,它这块呢,就是一个A了。然后这个呢,就变成一了,然后接下来呢,这块呢,诶,他又添加了一个B,它添加了一个二,然后一打印,诶这不就这个结果了。然后呢,第二个线程呢,接着执行在原有的基础之上呢,是不是就多了个C,这个呢,多了个三,然后呢,接着再进去,这多了个D,这多了个四。这样的场景。哎,那么刚才呢,我们说第一个线程先执行,执行完以后呢,执行的第二个,这就意味着第一个在执行完的时候呢,相对应的S2也好,S1好,这两个同步监视器呢,就都给释放了。
36:09
所以呢,你下边这个线程呢,才可以非常顺利的拿到这个S2和S1这个同步器。啊,注意啊,那么这个题目呢,如果说呢,先执行的第二个线程。再执行的第一个线程,那结果是什么呀?先三,哎,不对,先这个CD34CD12不是。AB是吧,然后3412诶这样子的啊,所以说有一类这个笔试题的话呢,就是把这个代码往这一放说呢,以上代码可能结果。呃,这块你就注意了,诶可能这是对的,还有刚才咱们写的这个是对的,那么还有可能呢,就是思索了。我们加上这个呢,是让这种死死的情况呢,出现的概率呢高一些。
37:00
注意,我只是让他概率高了。不是说呢,从无到有只能是一个量变,不是一个质变啊。他仍然呢有思索问题,只不过呢,这种概率更大,然后我们再去执行。你发现他这时候就僵持到这儿不动了。你看这个呢,是不是就我们说的四所了。这个永远解不了。啊,即使呢,我们这个sleep这个时间到了也解不了。因为呢,你第一个线程呢,是咱们这么找一下啊。嗯,把这个这都收一下啊。嗯,这个收过了。嗯,这个好像。视野范围之内好像看不全了是吧。你看这个线程的话呢,它就是握着S1这个同步加然器等S2。然后在你中间这个时间,你先S1,在你另外一个就执行了,就拿着S。然后他也sleep了,然后你上边这个醒了之后呢,你去拿S2的时候呢,他已经被这个钱能拿到了。
38:02
这个线程呢,这块呢,去等着S1。大家不就互相僵持了吗?那就构成了我们这个思索的这个场景。啊,那就接受不了了。是吧,这个呢,只能我们自己呢,给他去先解一下了。啊,注意,这是我们说的这个思索问题。好,这呢是一个死锁,然后呢,这块我又写了一个例子啊,为啥写这个例子呢?因为呢,可能有同学会说,说这还不容易看嘛,一看一个锁,一看另外一个锁,然后这俩一看反着来的,这肯定是有可能死锁,这个呢是我写的比较明显的哈,那我们再看一个不是那么明显的一个例子。哎,这呢,我就提前写好了一个代码。这个代码呢,可能大家觉得猛一看稍微有点乱啊,我给你剖析一下啊。这么着?我先把这个有些这个操作呢,给大家隐藏一下啊好。这样看,这呢是一个类A里边呢,定义了两个方法,这两个方法呢,我加了C了,这就是同步方法了,同步接然器呢,就是当前A的对象。
39:03
B呢,也同样的道理。然后呢,接下来我们在这个实现这个里边呢,声明了AB2个分泌量。这有两个方法啊,看这首先呢,我创建当前这个实现类的一个对象,这个对象呢,我们使用第二种方式放到外的这个形态,这块呢,叫四大方法,那么这儿呢,就一个分线程就出去了。这个分线呢,就接下来要执行这个run。然后呢,我们下边就通过这个DL呢去调一内的方法,这呢就是我们主线上的代码。相当于此时你把这个DR呢,就当成一个普通的对象就行啊,它呢调in相当于这呢是主线程,主线程调这个,分线程调这个。哎,这两个呢,就分别呢,开始去执行了,好,那么对于分线程来讲呢,它呢,其实里边呢,就是做一个事儿叫B点8A。啊,就做这个事儿,相当于分线程呢,就是调我们上边这个2A就调这个方法了。是分线程,主线程呢叫in里边呢主要就是a.fob。
40:03
相当于呢,分线程主要来调这个主线程呢,调这个。没问题是吧。这是两个线程啊,然后呢这呢整个就跑到这来了,对于这个分线程来讲呢,他先调这个的时候呢,他要握一个锁。这个锁是不是就是你B的这个对象呀。你注意我是通过这个。嗯,看那个分县长啊,通过这个B对象去调的,那这个锁其实就是我们这个B对象吧。啊,就是我们这个对象啊好,他呢要先握得这个当前B的这个锁,然后呢,他去调这个方法,调方法的时候呢,里边呢,它又调a.last了。A点呢,是不是就掉到这个方法了。这个方法也是个同步方法,那A呢,就是你传起来的这个A对象了,那相当于呢,又得握这个同步监视器。相当于分线的呢,是握着B的对象的时候呢,还得再去握A的。这是这个分线程,那么你也能想到了这个主线程呢,跟它正好相反。A去调fob,它呢先握A的对象,然后呢,在这里边呢,它又调B点拉呢,那又得握B的这个头器了。
41:09
这俩不就反过来了吗?一个是A的等B的,一个呢是握着B的等A的。那么中间呢,我还刻意加了一个sleep。啊,那么这儿呢,就等着是吧,就是如果你要先执行,你在这块你稍等等,然后另外一个线程呢,就他就先把这个给握住了,那就互相不就僵持了吗。那这块我们去做一个run。诶,这时候你会发现呢,这个程序你看在这儿就僵持着进不去了。这呢,也是思索。比如这种场景呢,似乎看着就更隐蔽一些,也就是说。咱们呢,在开发当中,哎,会调一些同步方法,那你调完一个同步方法的时候呢,你注意我此时呢,其实握着一把锁了。那你再调另外一个方法的时候呢,假设又是另一把锁。如果呢,还有别的县城呢,反过来那这个事儿呢,思所基本上就形成了。
42:00
但是你可能运行的时候呢,没出现哈,那只不过呢这个。侥幸是吧,这个呢,它握的时候呢,你这个呢,把这个握住了,等这个的时候呢,另外一个线程呢,恰好没有先握住它。就哎这样画应该哈,这是第一个锁,这是第二个锁,第一个线程你握住它的时候呢,在等它的时候呢,恰好另外一个线程还没有握住它。哎,所以呢,你就先执行了,然后呢,你释放了以后呢,它才握。哎,侥幸没有出现死错,但是呢,其实是有可能出现的。啊,要注意。好,那么关于思索呢,这个事儿呢,首先呢,我们就弄清楚了啊,那下个问题这呢,也是在这个笔试面试当中常爱问的是吧?诱发思索的原因是什么?怎么去避免死锁呀?对吧。来我们就看下边这个操作了。嗯,这个啊。诶,这呢,我写的说叫诱发死锁的原因啊,这块呢列了有四个点。那基于这四个点呢,我们去解决这个思索。啊,解决这个思索,那首先我们看都有什么原因导致的啊,首先呢,叫互市条件。
43:05
啊,这个呢是必然的,什么叫互斥呢?就我们这儿呢,是一个同步监视器了。啊,就我们说的这个锁啊,然后这个线程呢,去访问的时候呢,如果我握住了另外一个线程,你就握不住。这是必然的啊,如果说你握的同志这个还能过,那完了,那同步都白讲了。加同步的目的呢,就是为了让他握住,他握不住是吧,所以这个忽视呢是诶一定会出现的,然后占用且等待啊我们这个呢,握着一个同步加然器了,然后呢,诶他呢还在等待另外的,然后其他的另外那个呢,就是拿着呢,因为他的等待呢,其实导致呢,他就没法继续执行,这是这个,然后不可抢夺,你等待吧,你还不能说抢过来。啊,就是这呢是一个线程A啊,这呢是一个锁,或者第一个锁等第二个锁,这叫等待了,然后呢,另外这个线程B呢。他先把这个锁给拿到了。你说A,我能不能把它抢过来呢?不好意思,抢不过来,所以不可抢夺。然后下边一个呢,叫循环等待,就这种情况呢,会一直这样僵持着。
44:03
啊,就是怎么着也解不了了。啊,叫循环等待的一个场景。啊,稍微大家可能有的理解不是特别好啊,你看下边这个呢,就更好的去理解了。哎,我们要想解决这个思索问题呢,其实就破坏这里边儿的,呃,任意一个条件就可以了。啊,第一个叫互斥。这个事儿搞不定。啊,因为呢,我们要想解决安全问题,就是得互斥啊,这个呢拿到同步监视器了,另外一个呢就得等。啊,这个呢是呼市呢是改不了的,所以第一个呢,你就别想了,那第二个啊要占用且等待。那么针对这个的话呢,我们可以怎么考虑呢?你可以一次性的申请所需要的所有的资源。哎,这样呢,就不存在等待了,比如说你不是先拿到这个锁再拿这个锁吗?就是我一次性的让你全拿到别的县城呢,都不行。哎,都拿到以后呢,这块你顺利执行完以后,你释放了别的你再过来去用。这就解了。啊,这是这种情况,然后呢,要是从这个角度来讲的话呢,说不可抢夺。
45:04
说呢,呃,假设呢,我们现在呢,就申请了一个,我就申请第二个的时候呢,诶不是这个一次性都给你了啊,还是这样分着给,但是第二个时候你在这等也抢不了,那怎么办呢。啊,这时候很能抢,或者说呢,就别抢了,你自己这个线程主动的啊,如果呢,你握一个同步加然器,在等待另外的资源的时候呢,发现这个呢拿不到。哎,在指定的时间之内呢,你在这块等嘛,那就你把你现有的这个呢,你也退回去。就是你把你这块word这个锁呢,释放一下,你释放别人的话呢,不就不用等了,就能拿了是吧。诶,所以就这样,就是当你进一步需要获取资源的时候呢,申请不到了,你就主动释放你已经占用的资源。哎,让别人呢,先来操作。哎,不就不僵持了吗?就是说呢,你这块吃饭的时候呢,你拿一根筷子,等另外这根对方呢,也这样如此,这就思索了,那怎么办呢,你拿不到这个,你说那我把这筷头给你吧。哎,这就解了啊。好,下一个呢,叫循环等待,这个怎么解呢?我们把相关的这个资源呢,可以改为呢,叫线性的一个顺序,比如申请资源的时候呢,我们有一个排号是吧?呃,把这个排号呢,排号顺去以后呢,大家都按照这个顺序呢去获取。
46:09
那这样的话呢,不就也解了吗。不会出现的,你拿着这个,然后等那个那个呢,被别人拿了,别人拿拿的话呢,它也是按照这个顺序来获取的。也就不会出现这样的问题了。好,这个呢,大家理解一下。哎,满足以上四个条件的,会触发思索。啊,这是触发的情况,怎么解,就针对这里边儿呢,哎后边三个场景,哎,我们破坏其中的一个呢,其实就可以去解。啊,第一个呢,这个不现实啊。OK,那思索的话呢,我们说到这个场景呢就可以了。好,那思索问题呢就结束了,接着的话呢,我们来看诶同步呢,第三个问题啊,第三个的话呢,其实是跟同步呢并列的一种解决方案了。前面的话呢,咱们使用的这叫sized,这个翻译过来的话呢,就叫做同步了。
47:00
所以我们说同步机制,其实说的就是C的这种使用方式。那除了这个使用之外呢,说JDK5.0呢,还提供了一种方式叫做lock锁的方式。就是另外的一种解决线程安全问题的方式。那这种方式呢,怎么用啊,咱们先通过代码呢,让大家呢体会一下啊。这个用的话呢,自然而然,我们得先营造出来一个线程不安全的场景啊,这呢我们讲过实现,讲过继承,其实用哪个都行。哎,我来说继承吧,呃,因为这里边呢,呃,因为它天然的没有这个所谓的共享数据,呃,所以它处理起来呢,要更小心一点啊,那咱们就拿这个集成的方式来说CTRLC。Lock ctrl v粘过来。放到这儿了。嗯,这个名呢,咱们我改一下吧。哎,我就称为他叫这个lock的。它的一个测试。然后呢,这个位置我们改一个名。
48:01
哎,它的一个测试。那首先的话呢,我们看一看这个呢,是不是线程不安全的。回过来,嗯。是的,这时候行程不安全,然后我们先把它跑一下。OK,这里边儿呢,出现了重票了,这出现错票了,都出现了啊好,现在呢,我要把它改成限制安全的。这种方式的话呢,我们用的不是C了啊,用的叫做lock,这个lock呢,首先呢,我们可以从这个API的角度呢,先看一眼。啊,你会发现呢,它其实呢是一个接口啊。Lock,这个lock呢,它在哪呢?在这呢啊,你看它所在的包。带这个包是吧,当然还是这个包的一个子包啊。这个我们再看一眼啊,这个我我就直接进来吧。诶,它的这个包呢,你看java.u.concurrent.locks这样一个包,这个包呢,你看前面的这,诶这个位置啊,把它呢,首字母取出来是不是就勾。有谁呀?
49:01
对诶后边呢,我们要讲的一个课程呢,叫guc这个GC的话呢,其实就针对于这个包来讲的,这个con呢,我们翻译了不就叫做并发吗。所以呢,Java的并发编程呢,其实就是讲这个包里边儿的相关的API的啊啊,那么这里边儿呢,我们就提前相当于先预支一个。API叫做lock,它呢是一个接口,具体的话呢,你看有这样的一些实现类啊,然后在这个实现类里边呢,我们目前呢,想给大家呢,使用的是这个叫re lock叫可重入锁啊。这是它的一个实现类了,好,那这呢,我们就创建这个类的对象。OK啊。行,那么我们创建对象呢,我就写到这儿了。啊,这个我们就是一个。哎,你有他的一个对象啊。哎,就他好造了个对象了,这个对象的话呢,我们现在考虑它的这个权限修饰了,比如呢,我不希望在外部呢被访问,因为这个呢就是纯粹为了保证现车安全的,所以呢没有必要对外呢访问。
50:02
所以呢,我们就private了。然后呢,这个锁的话呢,其实我没有要求。接下来我们要保证它的现车安全问题。那多个线程呢?要共用同一个lock。共用同一个。咱们前边这块呢,呃,几个县城卖票票还得同一个加过sta是吧,那这呢,是不是意味着也得。Static一下是吧。哎,就这样啊好,那顺便呢,其实这个final呢,加一下也行啊,Final就表示你一定要复制,以后呢,里边就不要再给他去修改了,是吧,就得是这个对象唯一的不能动,OK。好,这个修饰完以后啊,这呢是咱们的第一步啊,嗯,下一步怎么做呢。诶下一步呢,注意我们这个锁定操作呢,不像同步代码块有一个大括号。哎,大括号里边呢,就是需要被同步的代码了。而这个log呢,它比较灵活。他呢,哎,只需要呢,用两个方法去限制。那两个方法执行当中的代码就是需要被同步的代码。
51:05
在我们这个问题当中,同步的代码就是这一段。那么在这一段代码之前,我调一个方法就叫做lock lock方法。哎,调这个方法。当这个代码执行完以后。我在这个位置。调一下叫unlock这个方法。啊,那么中间的这段代码就是我们之前放到同步代码块当中的代码。写完了是吧,来,我们去做一个。啊,大家看这时候是不是就是安全的。呃,安全的话,你发现这块这块呢有点问题是吧。哎,其实呢,就有可能会出现呢,我们锁定的时候呢,没有解锁的这种场景啊。啊,那么这块我们处理咋做呢?呃,其实这呢,也是我们接下来要说的一个事儿哈,这个unlock这个操作的话呢,我们其实要保证啊,它一定要被执行这样的一个场景,所以呢,我们需要呢,把它。
52:01
哎,确保一定要执行的话呢,是不是可以写到。分类当中是吧,哎,所以这块我们这样来处理啊,在这个位置呢,咱们加一个踹是吧。然后呢,把这个代码从这到这儿,咱们给它移上去。往后移一下啊,放到我们这个TRY里边,然后呢,这块我们来一个finally。哎,这么着,然后把这个代码呢,给他整过来是吧,这个呢叫unlock就是确保我们这个操作呢一定会被执行。OK啊行,然后呢,我们这时候呢,再去做一个。嗯,这个能看现在呢就结束了啊好,这个呢,就是我们说的这个叫呃,Lock的一个使用,稍微呢,我们再去去记录一下啊,这呢属于第一步叫创建lock的实例。诶注意这个log呢是个接口啊,咱们现在用的叫可重锁啊,它呢其实有好多实验类,咱们呢就不讲那么多了啊,主要呢,我们就演示一下用的它。
53:01
创建它的这个实力,这个一定要注意啊,需要确保。多个县城共用。对,同一个lock实例。这个一定要保证,也就意味着呢,我要是没有加这个static。咱们这个时候呢,豌豆呢,一共造了仨对象,家里呢现在就有三把锁。每一个呢,去lock都锁自己的,那就不好使。啊,还是有这个问题是吧,所以呢,这块一定要小心啊,这个呢,死呆立刻是少不了的。啊,那如果说咱们放在这个实现的方式里边,咱们还有实现吗?实现里边的话呢,如果你要是呃,大家共用的同一个的话呢,那个static可以考虑,就不用写了。但是在这个继承这里边呢,这个要接着加呢斯克啊。共用同一个lock实例,所以呢,这是我们呢,就是呃,需要考虑将此对象声明为。对声明为啊是static的啊,反正的话呢,就纯粹说呢,你不要改了。
54:03
是吧,就这个意思,所以这块你写成大写也行啊。好,这块我写的写的要需要考虑,就是我们在实现里边呢,有可能就不用static了,主要你看是不是唯一就行,这是第一个事儿,然后这是我们的第二个操作。哎,这个呢,就是要哎执行这个lock方法。这个呢,我们叫锁定啊,对共享资源的一个调用。谁调的方法了,现在就锁定这个代码了,然后这是我们的第三步是吧。一个叫unlock。哎,这个方法的话呢,它的一个调用。这个呢,叫。释放。哎,对,呃同步,呃对这个共享数据的。一个锁定啊。OK,那这个呢,就解锁了,解锁以后呢,其他的线程就可以来操作了。这个呢,就咱们对应的这几个步骤啊,执行lock方法。嗯,那么回过来啊,到这儿咱们把这个笔记呢,稍微的这样CTRL一下。
55:05
步骤第一步啊。第二步。然后第三步。这个呢,是咱们说的这个三步啊。诶把这个稍微提一下啊,咱们就结束了,这个信用寨的话呢,是我们之前讲的就要同步了啊,这个叫lock,诶这两种方式大家。觉得哪个好啊,或者有什么区别呀。嗯。这个咱们就过了啊,就是这块呢,就不深入的去讲了啊,这个呢,以后UC里边呢再详细去讲。嗯,哪个方式好啊。这个好。唠为啥唠好?辛德庆。
56:01
对,这个逆向思维角度来讲,他之所以入个新的,一般新的都好是吧。呃,要不的话呢,就不会引入新的了,当然了,呃,引入新的这个事儿呢,也不一定。以后呢,咱们看到这个JDK更新版本,每半年更新一个版本,从JDK9开始是吧,其实呢,也会出现一些不靠谱的新特性。啊,这个版本加了下个版本发现没有用,他就半年以后呢,及时的就把它删掉了,会出现的啊,但是呢,这个GP5.0已经是很早了。他至今呢,都没有把它删掉,那说明他应该是挺好的。对,所以从这个角度来讲的话呢,一般新出现的,而且呢,还没有在后续的版本中删掉,那这个一般呢都是好的是吧,诶所以呢,从这个角度来说,Lock呢要好一,当然呢,你不能这么说啊,所你说这俩为什么好,因为他新。这跟没说一样,还不如不说呢,我觉得是吧,啊那这个呢,诶确实情况呢,我们说这个lock要更好一点,这个呢,就涉及到他俩比较释放对共享数据的这个操作的话呢,方式不太一样。这个用袋子呢?
57:00
这个大家能。会写这个单词了不?得会默写啊。对。这个C共债的话呢,不管你是同步代码括号还是同步方法,它呢,都得是在结束那一对大括号以后,才会释放对同步然器的一个调用是吧。啊,不管。哎,是这个同步代码块还是同步方法。是吧,都需要呢,在结束。哎,在结束啊一对,诶大括号之后。哎,释放对,诶同步监视器的调用。的一个调用。啊,这个呢是它的一个特点,而我们这个lock呢。它的这个呢,我们说呢,是通过两个方法是吧,Lock呢是通过啊两个方法。诶控制呢,需要被同步的代码。啊,那这个其实我们说呢,就更灵活一些。
58:01
啊更灵活一些,实际上呢,还有更明显的这个lock的一个好处哈,这个lock的话呢,咱们说呢,目前就是一个接口啊,这个接口的话呢,它其实有好多的实现类,那就是好多种锁,那就意味着它可以实现更丰富的一些行为。读索呀,读写锁呀,写索呀是吧,哎,它呢非常的灵活,只不过咱们不细节去讲了啊,这个我们再说一下这个log呢,它作为接口。啊,提供了。在多种实现类。然后呢,呃,适合更多的啊,更复杂的这个场景。啊,这个呢,其实呢,在使用log的时候呢,它的这个效率呢,也会更高一些,但是这块我们就不真正去体现这个事儿了。啊,我们就这样呢,泛泛的这样一写,那杨呢,就是在我们以后处理的同步问题的时候呢,其实优先考虑呢,可以用lock是吧。诶,这个lock的话呢,其实在官方文档里边呢,其实也写这样的一个事儿了,你看哈,说lock呢,它的这种。
59:03
具体这种实现哈,它其中了更多的啊,更广泛的一种选择啊,这种行为呃包含呃,就相当于呢,这种行为呢,你用原来这种方式来讲,它提供了更广泛的这种锁的这种行为了。就是使用范围更广是吧?啊然后呢,下边有这样的对应的描述,其实这块就提到呢,它其实比这个log诶CI用的这种方式要更好一些。这个大家了解这个事儿就行啊呃,因为可能会出现呢,在笔试里边会去问啊,这俩呢方式有什么区别啊,当然现在呢,咱们还没有完全去展开,因为呢,Lock呢,我们没有讲更丰富的一些实现类,这个呢,相当于以后的GC呢才会去讲这个事,OK。嗯,这儿呢,我们就先说到这儿。
我来说两句