00:00
好,咱们上午呢,讲的使用同步代码块,或者是同步方法来处理继承thread类,或者是实现renoable接口中的线程安全问题啊,这句话有点长啊,是重点,呃,大家需要掌握,实际上呢,在这个代码层面呢,其实呃并不是说我们写了多少的代码,其实没有啊,但是呢,这个里边呢,涉及到关于这个呃,又翻来覆去的说了好几次了,涉及到我们这个一个呢叫共享数据,一个呢叫同步监视器啊,这里呢,涉及到相关的一些知识点,咱们已经说过了,就不在这多说了,需要大家呢去关注,虽然代码量不大,但是这里边这个思维量和这个难度呢是有的。好,这个呢我们就结束了,那这个完了以后,我们接下来呢,说两个问题,第一个问题呢,咱们在前面讲面向对象的时候呢,提过单立模式,单立模式里边呢,其中这个懒汉式呢,是线程不安全的,那现现在呢,咱们讲了这个同步了,咱们就可以呢,把之前咱们讲的这个改成一个线程安全的。怎么?没有收到,那你网线其实你插上就行。
01:02
有吧?嗯,好,就是呢,我们可以使用这个呃同步的方式,把咱们呢,前面讲的这个懒汉式呢,改成一个线上安全的,哎,这是第一个点,另外一个点呢,就是我们这个同步的话呢,是有好处的,就是解决线场安全问题了,它的相对于局限性或者叫不好的方面呢,就是效率比较低,这是其一,其二的话呢,就是我们有的时候呢,可能会导致一个另外一个问题,就叫做死索,那咱们把这两个问题呢说一下,首先呢,咱们来说第一个问题就是改这个懒汉式啊嗯,咱们呢,在这个src下呢,再去新创建一个包啊,Com点艾特硅谷点JAVA1好,这个Java一下呢,咱们说一下这个,嗯懒汉式的这个问题啊,新创建一个class,这呢不妨我们还以这个bank为例吧,这个银行来说明一下,这呢,我们在这写一下说使用哎这个同步的这个方式啊,或者要使用同步机制啊,将。
02:04
这个单例模式中的这个懒汉式,哎,改写为诶线程安全的好,那我们来举这个例子,咱们讲的时候呢,是主要呢是针对这个bank来讲的,所以我这呢去class一个bank,这个类呢,我们要求它是一个单类的,相当于呢,只能够创建一个当前类的实例,咱们说有几个步骤呢,是必须的啊提到了这个构造器的问题,构造器的话呢,我们需要给它私有化。呃,当前的这个构造器的话呢,咱们也没有当前类提供一些属性啥的啊,所以我这儿呢,就呃单纯的来说这个单类了,在具体的开发情景当中,有的时候我们这时候呢,需要给这个类呢提供一些属性,那你在构造器当中呢,可以给这个属性做个初始化啊,这是咱们就不提供了,这是一个,然后接下来呢,我们去提供了一个叫私有的static。
03:00
啊,顺便大家还得回忆一下这个懒汉是怎么写的static,然后呢是一个bank类型,我叫incense吧,懒汉是是个now,然后接着呢写一个public static返回,这是bank,我们就要get instance,诶,你看这呢,他都给我写好了啊。行这么着,那么在这个方法当中,我们呢,去判断一下这个当前这个incense呢,是不是个闹,说如果这个incense呢,它是一个闹。那咱们呢,就需要给他去实例化一下,诶,等于我们去new一个bank。好,那你完以后呢,你在这给大家return一下。哎,那如果说呢,后来再调的时候呢,已经有这个实例化的对象了,我们就不用再去进去了,所以这个呢,干脆我就写到外边得了。这呢是咱们这个方法,整个这块呢,我们把它叫做一个懒汉式,那这块呢,测不测试其实不重要啊,关键呢,就是我们这儿呢,体现的是这个叫单力模式,这是咱们前面讲的两项式了,没问题,那么现在呢,我们来回顾一下当时讲的这个线安全问题,嗯,当然了,咱们现在这个代码这个这个只是说呢,咱们讲的这个线上问题的这个实体啊,就实际的这个情况啊,这个还没有咱们给他整到一个现成的软方法当中,你可以理解为呢,就是咱们这个操作。
04:24
Get instance这个操作呢,是我们在run方法当中掉了。诶,你可以这样子去理解啊,我就没有非得营造出来一个多线程的这样的一个这个场景了哈,呃,只是呢,我们举例这里边的核心代码,呃,这个我们创建这个多个线程,他们呢,各自去调run方法,在各自的run方法当中,各自呢又调了get instance这样去理解,那么此时呢,就意味着我们有多个线程可能来调我们这个方法吧,比如说就两个,那其中一个线程呢就进来了,进来以后的话呢,这个instance我们首先判断一下,假设呢,首次调用,那么这个incense呢,显然它是一个no。
05:05
哎,显然呢,它是一个nu,它要是个nu的话呢,我们就会进入这个一夫一句,那么一个线程呢,就进来了,进来以后呢,我们说在这个位置呢,是可能会出现阻塞的。啊,即使你不阻塞呢,我们也有可能CPU呢,就切换到其他的这个线程里边了,那么当前这个线程呢,就出现就绪状态了,啊也是有可能的,总之呢,就是我们这块呢,就是停下来了,那么在这个停下来的时候呢,我们另外一个线程进来了,另外一个线程进来的时候呢,同样的判断incense此时呢还是no,所以呢它也就进去了,所以呢会导致咱们这个ince这个变量呢,回头会先后啊这个被两次复制过。啊,那这呢显然是不对的,这就是我们所谓的叫线程的安全问题,哎,之所以会出现线程安全问题,是因为我们这儿呢有两个线程,而且呢,它还有共享数据。那此时呢,我们的CE就相当于是共享数据了,我们针对这个CE操作呢,一方面呢是判断它是不是no,另外一方面呢,给它做了一个赋值,这呢都算是对共享数据的操作。
06:10
哎,至于说这个return这个呢,呃,其实你算是操算不算操作呢,你要算他这个有吧,他到时候用这个共享数据了,但是这呢谈不上是什么操作了,你像这个判断呀,或者这个赋值啊,这都是明显的操作了啊,诶这是这样的一个情况了,所以说此时呢,咱们为了保证这个懒汉式是安全的,那就需要呢给它做一个处理,目前呢,咱们就讲了两种方法啊,一种呢叫同步代码块,一个呢叫同步方法。那如果呢,简单来处理,我们直接呢,在这个方法这个层面上,咱们直接给它加上一个叫S。此时其实呢,就已经是一个现场安全了,你想是吧?啊,当我们两个线程来调这个get方法的时候,因为呢,我这是一个同步方法了,那这个同步方法的这个锁是谁啊。
07:06
谁呀,是不是bank.class呃,因为咱们这是个静态方法啊,所以说呢,静态方法的同这个静态的同步方法,它的这个锁呢,是当前类本身,因为咱们上午也提了,说这个类本身也充当了是一个对象,咱们后边到反射的时候呢,再给大家详细的去说啊说这个事儿,呃,你就好比说咱们这个有一个有一个词叫什么叫概念是吧?说概念什么意思呢?啊,就是用来解释说明,呃,你某一个词的那个。那个那个内容叫概念,那概念本身它是不是还也是一个词啊,是吧?哎,什么是概念啊,你把那个描述了,那概念本身也是个词,还得需要东西来解释,就是说类呢可以造对象,那类本身呢也是一个对象,呃,这个有一个这样的一个一个这个一个事儿啊,这个咱们后边反射的时候再详细的说,那总之呢,就是所一定是对象,这个时候的所呢,就是我们的这个半类本身,那么这时候两个线程,那么就看谁能拿到这个锁了,比如说我们这个线程一。
08:10
他就拿到这个锁,锁就是我们这个当前这个类啊,他就进去了,他进去以后,哎,我们在这里边的个执行着啊,那么在这个执行的过程当中,别的线程呢,都进不来,当我这个return执行完,我这一段大括号这个方法出去以后,这个锁就释放了,所以释放呢,我们这个,诶另外一个线程呢,就可以进来了,它进来以后,由于我们线程一呢已经给扭过了,所以后来再来的这个线程呢,它在判断的时候呢,都是false了,所以直接呢就return出去了,诶使得呢,我们整个这个过程当中,不管有多少个线程,不管有没有阻塞,诶只是被扭过一次。在这呢,就安全了。这就可以了啊,就是你这样写也行,这种写法呢,和我们在这个里边,这个里边呢,我就相当于是不是把它整个包起来,用个同步代码块啊呃,这个怎么做这个事呢?咱们在这个eclipse当中是不是有个右键叫surround with是吧。
09:03
呃呃,这个当时咱们讲异常的时候呢,这边一包有一个叫strong with,直接呢就包起来了是吧,在这呢也有一个,这呢右键没有了,用谁呢?Out shift z,那这不就出来了。啊,原来呢,这个try catch呢,你可以如果你要处理异常就用它,这呢我们加一个S袋点一下啊就放在这了,放在这的时候呢,我们需要填一个显示的一个同步锁了,啊写这样是不是就写这个bank.plus嗯,这种写法跟咱们刚才在这个位置加上一个static,其实是呃,加上一个sychized是一样的,哎,总之呢,就是都把它呢包起来,作为一个整体啊在这个过程当中呢,是一个单线程的,好,这呢就是咱们就改好了,但是这种改法呢,我说下效率不高,所以我这写成了叫方式一。虽然呢,它是一个线程安全的,但是效率呢稍微差一些。怎么体现这个叫效率稍差呢?
10:04
嗯,对,这个时候呢,我说的效率稍差,还不是说呢,因为同步出现了稍效率稍差了啊嗯,这个呢,已经本身就差,这个咱们就不用多说了啊,那么也就是说我在同步的基础之上呢,我还可以让它效率再高一些,你先看它为什么现在这个差,比如说我们现在呢,有两个线程,或者说呢,再多几个也行,那么我们第一次,第一次呢,进来的时候呢,比如我这个线程一呢,哎,拿到这个同木锁了,我就进去,我进去的时候呢,相当于咱们就把这个对象呢,创建了,我就出来了,然后第二个线程呢再进去,第二线程进去的时候呢,其实它什么也没干,就把这个已有的这个对象呢就拿拿着,然后就出去了,后边呢,比如我们还会有很多的线程假设啊,很多的线程都进来,实际上呢,后边这个线程呢,按说呢,你们就不用在这等着了,对我这个时候呢,你像这个对象是不是都已经造好了,你们直接呢,是不是就拿着这个对象出去就完了。
11:05
或者换句话说呢,大家可以把这个判断和这个new看成是对共享数据的操作,这个return呢,可以不看成是对对共享数据的操作了,你直接进来以后拿着这个对象就出去就完了。相当于后边这些人呢,其实没有必要在这干等了。嗯,我再举一个生活中的例子,比如说呢,这个,呃,前几年的时候比较明显,就是苹果手机刚出的时候呢,这个一机难求,所以出现很多这个黄牛啊,现在呢,不一样了,那现在这苹果呢,这个得得得得降价是吧?啊已经不是那些年的这苹果了啊,就是当年印象特别深刻的时候呢,就是呃,苹果5S 5S当时呢,出了一款这个叫哎金色土豪金是吧?呃,从5S这时候呢开始出来的,为什么我印象很深刻呢?因为我买了一台,而且我这一台呢,是整个这个我住回龙观这块啊,整个回龙观地区的第一台5S啊,我就很早去预定了,呃,所以印象很深刻啊。另外一件印象深刻呢,是因为过年回去的时候呢,给丢了啊,就是这个回家的时候呢,感觉还挺得瑟的,结果在火车上就被偷了啊,这个就是石家庄火车站啊,啊对,这个在网上已经都比较有名了,是吧。
12:21
看就是石家庄火车站,那时候呢,确实比较乱啊,很容易丢东西啊,所以网上有好多这个呃段子,就说这个以前是昆明还是哪出过这个火车站砍人事件是吧?啊说这种事情呢,在北京不会发生,那北京那个刀呢,根本就掏不出来是吧?啊,地铁上人太多了啊,掏刀子都拽不出来啊,然后到石家庄也不会出现,因为一掏诶刀呢没了哈,哎,这就这个比较夸张一点,就可见呢,石家庄这块确实是,呃,那时候还不行啊,现在呢,华了个火车站应该是好多了啊,那时候就丢了啊,就是咱就比如说拿这个诶土豪金是吧,土豪金呢,现在这个店里边呢,就只有一台啊,就只有这一台,然后现在呢,很多人都要买啊,大家在这排着队,排了这个好几百人,这个队伍啊,都要买这个土豪金啊,这个一就只有这一台,你好比是这个单利一样,现在的话呢,这个第一个人啊,好几个人都要抢啊,第一个人呢,就是先抢到了,他就先进去了,进去以后呢,诶一看哎有手机,他呢就。
13:21
把这个手机给买了,买了以后呢,他从后门出去了,走了是吧,走了以后呢,其实这个时候呢,这些人呢,你就别让人家干等了啊,你在这块大家啊,后边这个人啊,出于这个现车安全的考虑,一个一个进去啊,进去以后一看说告诉他说不好意思没了啊,这哥们再从后边走,然后在后边再来个人,再一个一个出去,这个效率很差,哎,就是当你其中第一个人把这个手机买走以后呢,这个时候你在门前的立一个牌子,就说呢,告罄是吧,售空,这些人大家就散了就得了啊,就省得再去进到我们这个店里,相当于就我们这个同步代码块报的这个代码了,哎,直接呢大家就散了就完了,所以说呢,我们这个效率呢,稍微差一些啊,那么怎么就能让它效率高一些的呢?实际上也很简单,就是相当于咱们这立个牌子,就告诉这些人说没有了是吧,你你就走了就得了,那这时候呢,我们可以怎么办呢?哎,我把这个呢先注释一下啊。
14:21
哎,这是一个方式二,嗯,我们呢,在刚才写的这个代码的基础之上呢,外边我再给它包一层一。叫呢,Instance等于no。哎,然后呢,我把它呢扔进去,扔进去的话呢,这个return呢,咱们就别往这放啊,CTRLX一下我就拿到这个最外边了,这呢我就不认为它在操作共享数据了啊,这个你就你也没有对它进行修改是吧?啊,也没有进行一些判断之类的啊,所以我就呢直接写到这这两行呢,认为是在操作共享数据,我说呀,现在这种写法呢,效率就比较高。
15:00
来大家看一下,为什么对现在的话呢,我们有好几个线程过来啊,这块因为你没有加同步,所以就都进去了,这块呢,是不是判断一下这个时候呢,也都能进去啊,因为你这也没有涉及到同步的问题啊,所以就有好几个线程可能都会判断它的时候呢,刚开始都是no,那现在呢,比如说都在这,都在这的时候呢,看到谁能抢住了,假设我们这个线程一抢到了,线程一进去呢,New了,扭完以后呢,他就拿着这个new好的对象出去了,这两个线程呢,是稍微得等一下。他俩呢,稍微得等一下,因为进去以后呢,发现已经不是闹了,哎,他们就拿着现场这个再出去,他俩呢,呃,似乎是多等了一下,但是你要是在后来的这些现场,你再进来的时候呢,我们判断这个if。哎,他就已经不是闹了,就没有必要在后来的这些线程呢,再去进入这个同步代码块了,你呢,一进来发现我们此时已经不是闹了,你就别进入同步代码块等着了,大家呢,直接拿着这个in森就出去得了。
16:01
哎,所以呢,比我们刚才的方式一呢,效率要稍微高一些啊,就是这样的去写啊,效率稍高。啊,效率啊就更高吧,这样行,那这样的话呢,我们就把这个呃懒汉式改成了一个线程安全的,呃大家呢,如果在笔试当中出现了说让你手写单立模式啊,人家不管说啊没说你都得写一个线程安全的啊,所以呢,你要么呢就写那个鄂汉式,还是原来的写法,要么呢就写这个懒汉式啊,就写我们这里边建议呢写方式二啊就可以了啊。
我来说两句