00:00
大家好,我是海波老师,我们继续来讲Java中的线程操作,我们这里呢,给大家讲一下我们现成的安全问题,我们这里来拷贝,拷贝以后呢,我写上一个七,然后把这个改成叫safe,我们点击OK,点击完成以后呢,我们这里给大家演示个小例子啊,把这个去掉,去掉以后呢,我写上一个class,我们就叫U7就可以了,好,写完之后给他创建一个我的string,给他一个name可以了。写完了,我们这里呢,把这个去掉,我们来叫做线程安全。然后呢,我写上一个叫U点七,然后呢,等于U等于new,咱们的U点七就可以了,好写完了之后呢,接下来我们给它来创建个线程,写个T1,然后呢,我们这里写上一个new,然后写上一个thread,我们里面啊采用我们特殊的处理方式,我们给它写上,它写完了以后,我在这里干嘛呢?我就写个U的点。我们这里写上它叫you.name它等于张三,OK,然后呢写上thread.sleep然后呢给他一秒钟行了,然后呢给它加上我们的try cat处理一下。
01:06
好了,我们check开捕捉一下,捕捉完成以后,我们打印一下我们当前的u.name就这么简单好了,然后呢,让这个现成的我们给它启动,叫T点我们的start,好,这是我的第一个线程,接下来呢,我再拷贝我们的第二个线程,所以来我们写上它,咱们叫做T2。然后呢,把它改成我们的李四,那么接下来呢,我们在这里面写上它,我们叫T2.start,好,最后我们写上它打印,打印以后我们叫做主线程,咱们叫主线程执行完毕。好,同学们,我们现在呢,就给大家演示了一下,我们两个线程以及主线程他们之间的一个执行,我们这里呢,把张三给他,然后打印张三,把李四给它打印李四,这就是两个线程它的作用了,那现在我们就运行一下,我们看看我们的主要目的呢,是想看一看我们哪一个线程先执行,是张三这个县程还是李四这个县程,还是我的主线程,对吧?好了,同学们,我们现在执行一下。
02:07
你会发现我们当前的,诶不对呀,老师,我们这里不是有个张三的线程,有个李四的线程吗?为什么我们在当前的处理当中全都是李四的呢?哎,我再执行一下,我运行。运行以后看结果,你会发现它其实还是李四,诶老师不对呀,为什么没有张三了呢?这就是我们所谓的线程安全问题,这个呀,咱们光这么看找不到问题的原因,所以呢,我们得通过画图的方式给大家来看一看,我这里打开我们加入的线程,我们在这里呢,咱们往下来给大家呢,把我们的程序咱们给它画一画,首先我们先明确一个概念,就是说我们的Java,它会对每一个线程创建一个占内存,我们先说一下。Java它会对每一个线程,它创建一个占内存。所以我当前大家会发现我们这里会有一个主线城,还有个张山县城和一个李四县城,所以呢,我们这里创建我们三个县城的,所以我们这里创建我们三个线程的站内存,所以我们这里来把这个呢,我放到这边,这个呢我们就叫做主,诶所以给他一个红色,然后呢,我们这里来复制,复制以后我们来放过来,我们给他一个黄色。
03:19
好了,然后呢,我们这里再来拷贝,我们给他一个绿色。可以了,现在呢,我就是三个线程,三个线程的话,我们当前就是主线程,所以我写上一个主线程好了,然后呢,这里呢,我就写上张山线程,哎,咱们写上它叫张山线程,然后呢,我们这里呢,写上一个我们的李四线程好了,那我写完了以后,大家会发现我们当前的三个线程啊,首先他们都开始执行了,但是由于啊,我们的张山县城和李四县城呢,它里面都有休眠的代码,也就意味着它启动以后,它马上休眠一秒钟,他也马上休眠一秒钟,就意味着他们的打印语句都是在我一秒钟之后才执行的,而我的主线程根本就没有做任何的休眠操作,所以也就意味着它的执行是非常快的,一秒钟对于咱们来讲比较短,那么对于计算机来讲是非常长的,所以我们的主线程先执行完,这个是对的,所以啊,咱们现在的结果当中运行。
04:16
我们当前的主线程运行是没有任何问题的,但现在的重点呢是为什么我们这里一个是张三,一个是李四,打印的结果全都是我们的李四呢?这个原因在哪里?原因就在于这个对象是同一个,我这里拷贝,拷贝以后呢,我把这个放到这边,我假设它就是我们的堆内存,因为我们创建的对象应该是一个堆,所以把这个去掉,去掉以后在这个堆内存当中,我创建一个我们的对象,所以把这个我们拉过来,拉过来以后我这里给它一个颜色好了。放完了以后我就写上U,同学们有没有发现这就是U了,然后呢,我们来看一下,在程序当中,我的这个线程叫张三线程,张三这个线程访问的是这个对象,所以他把这个对象的属性变成了张三,所以咱们当前的这个里面有一个属性,它有一个属性现在改成叫做张三了,诶,我们写上它,所以我们把这个拉过来,所以当前UR的这个属性变成了张三,我们的箭头应该指向它,所以张三的线程应该指向这个对象,没有任何的问题,同学们好。
05:22
那然后呢,我们的理式的这个线程呢,也把U的对象,同学们这两个U的对象可是同一个U的对象啊,所以既然是同一个U的对象的话,那么所以它里面的引用应该是同一个,这个没有问题吧,同学们,既然两个线程指向的是同一个对象的话,所以我们理氏线城啊,把它的名称就成了我们的李四没有问题啊,所以这个张三就被改成了我们的李四,这个大家能不能明白?如果能明白的话,这就意味着我们在执行一秒钟的休眠之后,我们的打印,诶,我的这个线程打印,它打印的话,箭头指向的是U的,它不就是李四吗?而我的这个打印的话,那么它不也是我们的箭头指向它不也是李四吗?所以我们的两个线程打印的其实是同一个对象,你也打印,我也打印,你也改我也改,不就出现了冲突吗?所以啊,同学们,什么叫线程安全问题?这里我们总结一下。
06:23
所谓的线程安全问题。它其实就是多个线程。多个线程在并发执行时。修改了叫共享,内存中叫共享。共享对象的属性导致的数据冲突问题。这个我们就称之为叫线程安全问题,大家看一下我们当前啊,就是线程你也在运行,我也在运行,然后对共享内存,什么叫共享内存啊,同学们大家看一下我们刚才说的,我们的这个占内存是独享的,为什么?因为Java会对每一个我们的线程分配一个占内存,所以我们的占内存是独享的,但是我们的对应内存是共享的,你也访问,我也访问,对吧?诶,所以这就叫共享内存。
07:15
而且呢,我们的对象,大家有没有发现这个对象大家访问的是同一个呀,如果你访问你的,大家看一下来,你访问你的,我访问我的会有问题吗?不会的,恰恰呢,是我们两个人访问的是同一个对象,就导致了它的错误,还有一个我们是修改了它的属性。大家看一下,你是你把它改成张三,我把它改成李四,那所以同一个对象它的属性被修改了,当然就会有问题,对吗?但是大家想一想,如果我们不是修改呢。如果我们是查询呢,那么你也查我也查,咱们俩就不会有任何的冲突,对不对,诶,所以啊,我们之所以会出现这样的问题,其实主要就是修改了共享内存中共享对象的属性,诶,才导致的这种数据冲突,这个就叫线程安全问题,好了,同学们这个能不能明白?
我来说两句