00:00
那么将来呢,说继续往下看,我们这个加va塔线程呢,算是高级的一个部分,我们说原来我们用多线程对吧?多线程的目的是什么呀?或者说我们为什么要用多元程的,原来那是要提高效率啊,对吧?我们说我们应用多元程的目的啊,那就是提高效率,换句话说,我们利用多形程是不能尽可能的去利用CPU的资源呢,对吧?或者说尽可能的去利用系统的资源,但是大家要切记,如果说我们的线程如果使用不当的话,不仅不能提高效率,反而呢,它的性能会更低,因为我们说多线程它的开销实际上要比单线程要大的,因为我们说多线程,它多线程它涉及到一些,比如说线程之间的调度,然后呢,涉及到这个呃,CPU上下的内个切换,对吧,包括线程的创建和消毁,呃,消毁,甚至这个头部等等,那是不是都是。
01:00
多线程涉及的问题啊,而单线程是不是就不涉及到这些问题,所以说我们说如果多线程我们应用不当,对吧,那反而不能提高效率,那因此可能会更加的降低效率,那么实际上在建立个1.5以前,我们对现成的一些操作呢,可能无非也就是说对于这个同步,可能就是有一个叫做SNE关键字,然后还有一个叫做world,对吧,那那个呢,之前咱没学一会会学对吧,那J1.5以前可能也就是这么多,但是这节K1.5以后,那么Java给我们提供了一个非常强大的一个包,叫做java.uQ点concuent包,这个包里边提供了大量应用于线程的一些工具类,对吧,可以供我们在不同的需求上进行应用,对于多项做多线程的一个支持非常的强大,那么接下来呢,咱们的这个课程呢,就是主要学这些,那么我们说要想合理的应用多线程,我们必须呢,对一些。
02:00
那原理性的东西要有一定的了解,我们才知道在什么情况下我们用哪些就足够了,而不是说只要有多元长的问题我就加锁,那锁得耗费的性能是不是很大的呀?以前跟大家也说过,对吧?比如说我们说在什么情况下应用什么能利用最多最多的资源做更多的事情,那么咱们在这这个章的主要内容呢,可能就是说,比如说叫做内存的可见性啊,CS算法呀,包括所分段机制啊等等等等这一系列的问题咱们都会说到,那么一个一个来先说什么呢?先说第一个叫做what tell,这个关键字之前咱们没提过,那么相应的呢,它涉及到一个叫做内存可见性的问题,过来我们试一试。首先我在单建的项目叫做guc UC-D01。
03:01
对,创建一个包叫com,点艾特V 5.jc。来,这叫ile volaile对吧?Volaile黄键字对吧?那么下边呢,我们就讲一下这个vola关键字说will volunteerile关键字,说这个关个关键字,它是用来解决一个什么样的问题的呢?那么咱们先回顾一下原来的一个小知识点,以前咱们做过类似于这样的一道练习,咱们回顾一下啊,比如说thread DEMO,让他是去implementment,来个random IB,然后呢,实现这个抽象方法,咱们原来写线程的时候,是不是搞过这个类似一个标记对吧?当时咱们写那个什么,往一个集合里添加100个数对吧?然后呢,让他税3万,然后我们搞个标记,这个标记过来之后唤醒这3万还记不记得事吧,对吧?大概是这么个意思,咱们就简单的把这个练习写一下,说首先呢,是不有个flag的标记啊,初始值为false对吧,对吧,初始值为false。
04:07
那么相应的呢,我给这个呃,标记呢,提供一个get side的方法,这没问题吧,然后呢,在这里呢,我们说大概就做了这样一个事儿,比如说把这个在某种情况下,我说把这个flag变为处了,对吧,然后让他之后是不是做一些事情呢,对吧?比如说当他到这个时候呢,我们说flag是不是已经变为处了呀,我拼上这个叫做is flag的值能听懂吗?对吧?那在这之前呢,我们为了更加明显的话,我要让它延迟几秒钟,比如说来个200毫秒,是不是让它顺200毫秒啊对吧,200毫秒来个直行,把这个flag变为to,然后呢,在这里输出一句话,这是不是就是我现成的功能,那么这个时候呢,我过来进行应用一下,首先呢,我来个叫做ledread DEMO,对吧,叫TD等于又一个thread DEMO,然后呢,我是不是就启动线程访问它呀,对吧,那就又一个thread。
05:07
把TD传过来,点start对吧,启动,然后之前咱们是不是做了个这样的事,在这里判断well to,我们说循环的判断吧,说如果你听1.isflag的结果为真的话,那么我是不是就c out这句话对吧?大家注意啊,我是不是就打印一个横线呢?对吧?然后甚至我来个让我这个循环结束,注意我这个现在我问问方法,现在我这里有几个线程,一个是一个是子线程,一个是我们线程嘛,是不两个线程会同时执行啊,对吧?也就是说这个线程的作用是不是给它改值对吧?给它改值,而我现在问线程的作用是不是去读值啊,然后反复的去问处去读,只要当FLY值为处时,是不就打印横线,然后break结束这个循环,那么接下来我们看看到底这个横线会不会被打印,以及这个外要处能不。
06:07
能结束的了,听懂吗?那么这个时候右键运行。注意看结束了吗?首先说这个flag是不是两个线程的共享数据,这明显是两个线程共享数据吧,那我发现这里flag不等于true,说明这个flag是不是已经变成true了,然后相应的我在这里甚至你都打印flag是不是已经是等于true了呀,那也就意味着flag都是true了,我这里反复循环着去判断对吧?去获取flag的值,发现它成功了吗?是没什功吧,意味着貌似这里的获取的反它的值是处吗?说不是啊,那你觉得这个问题奇怪吗?不奇怪,你这不奇怪,我还没办法继续进行了呢,对吧?那么我们说这个情况他为什么会发生呢?你这都把我的思路给我打乱了啊,好了,我们说按理来说他们访问的时候共享数据啊,两个线程应该共享的这个flag的数据,那一旦一个线程说把它变了,另外一个线是不是也应该变呢?说为什么会产生一个线程它已经是错了,另外一个线程发现是不还是false对吧,发现它的值还是false,那这时就涉及到一个叫做内存可见性的问题,说什么叫内存可见性呢?我们说实际上呢,当我们程序运行对吧,说GM会为每一个线程或者每一个执行任务的线程,它都会分配一个独立的缓存,用于提高效。
07:51
听懂了,这GPM会每一个线程都分配一个独立的缓存,缓存用于提高效率的,那也就是说什么意思呢?我们说当程序运行以后啊,它的结构大概是这样的,对吧?首先它有个叫做储存,这个储存你就可以把把它理解为我们的对应内存。
08:11
听明白对吧,首先有个储存,那么别的不说,我们说这个Fla的数据,这是共享数据吧,它首先是一定在这个储存中存在啊,那也是刚开始的时候呢,它在这里有个flag值,F flag以及是不是等于false啊,对吧?然后呢,现在我是不是启动了两个线程同时去操作这个共享数据,对吧,是不是一个是读,一个是写对吧,对吧,哪个是读。慢线程是不是在读啊,是不是读读数据啊,我这个非线程数不据写就是改值啊,对吧?那么比如说我们说这个呢,前面这个就是我们的线程一对吧,线程一后边呢,是我们的这个main线程,是不是主线程啊,对吧?比如说这个叫做main线程主线程对吧?那么现在是一个什么情况呢?刚才我们让它晚了200毫秒,是不是意味着是不是它先执行的呀?对吧?这个分线程先执行,那么我们说当线程一和线程和这个慢线程在执行的时候,我们说每一个线程它是不是在这里都有一个独立的缓存,是不是都有独立的缓存呢?也就意味着当线程间要对主存中这个数据这个值进行改变,或者对这个共享数据进行改变的时候呢,它实际上有这么个过程,先会把这个共享数据读到我的分线程中来,对吧,是不是读到我的线程到。
09:41
缓存中来呀,然后呢,不是才把它改掉,是不是才改值啊对吧?然后才改值,改完了之后是不是把flag变为了to,对吧?然后因此同时将来他是不是要把这个改好的值再写回到主存当中去,对吧?我们说这个程序这个线程上的改值,或者说数据的运算,那是不是在缓存中执行的呀,对吧?那么说他改完了以后,将要把这个值写到主存当中去,那么在写之前,另外一个main线程是不是来了,Main线程一来,那么实际上它读取过来的数据,此时的flag为什么东西作为false啊,那这个时候呢,Main线程把主存中的这个数据呢也给读过来,但是是不是此时为false啊对吧?此时为false,那么紧接着会造成一个什么情况呢?当内线程把这个读过来以后,是不是有可能线程一数据就写过去了,写过去以后注意线程一把这个注。
10:41
打印与此同时是不是来的应该是先写,写完之后是不是来个打印呢?它是不是就打印flag此时已经是true了,但说慢线程此时拿着是不是first,注意慢线程啊,它我这里是不是用了一个while处,这个well处,它实际上调用的是你系统比较底层的一些代码,它的执行效率非常的高。
11:04
是慢对吧,麦这个V处处它实际上在调用的是相对来说比较底层代码,它执行效率非常高,高到什么程度呢?高到它有这个main线程都没有机会从主存中是不是再次获取一次数据啊,它是不是一直在这里反复的无限的循环着,正常来讲,如果你要是稍微的延迟一点,他说就能从这里把重新组存中的数据再读过来啊,但是由于Y要数执行的速度非常的快,以至于他没有机会去再次读取一次数据,他反复在这里执行的flag一直都是什么呀,都是不。对吧,对吧,那这就是一个内存可见性问题原因,产生这种情况的原因就在于两个线程在操作共享数据时,对这个共享数据的操作彼此是不是不可见呢?这是不是内存可见什么问题啊?两个线程是不是都有独立的缓存呢?它们之间对于数据共享数据这个操作彼此是不是不可见,数据权见呢?所以说就产生了这种叫做内存可见性问题,对吧?说内存对吧,可见性问题是指对吧,当多个线程访问共享数据对吧,或者不能说访问操作操作共享数据时,那么彼此是不是不可见的。
12:34
不可见,因此就产生了这种叫做内存可见性问题,对吧?我们线程拿到的是一直还是外钥处拿到那个外要处拿到的是不是flag等于false啊,而实际上flag值此时已经是处了。能搞定吧,对吧,但是大家要切记的一点就是现在人家这个flag是共享数据呀。听懂吧,那这明显是不是有问题,这明显是有问题的,对吧,这明显有问题,那那那这个问题怎么解决呢?那要用我们目前所说的这个办法呢,用什么能解决,用锁同步锁是不能解决啊,同步锁它能保证每次都给你刷新缓存,我来个这个single是不是来个TD啊,把我读的时候,我是不是给他刷新一下啊,让它重重复。
13:26
让它重复的到主存中去读吧,读数据,那这个时候右键注意看看效果。结束了吗?数据结束了呀,跟刚才是不是明显效果不一样了呀,对吧,我们说同步锁呢,确实能解决这个问题,对吧?它能保证数据的一个及时的一个更新,或者说我们说内存它会刷新一下,一会老人锁,但是呢,我们说用的这个锁就意味着什么呢?就哎,那也就意味着效率非常的低,低到什么程度,我们说那么多个选,如果说多个线程来访问这段的话,那它是不是就涉及到首先要判断所呀,对,那相应的如果一个一个线程正在持有这个锁,另外一个线程过来一判断,对吧,发现有有人在占用着这把锁的时候,那这个时候它是不是就得阻塞了呀,阻塞被挂起,那就涉及到下一次又得等到CPU再次给他分配任务,他是不是再来执行,是不到很多问题对吧,所以说你加锁这种效率那是不是最低的对吧,那是极低,那么我们说那现在我不想加锁,因为枷锁。
14:33
但是现在这里又存在一个内存可见性的问题,我们该怎么办呢?这个时候就有了一个叫做what tell关键字,所以说说这个关键字它的它的作用是什么呢?它就可以保证对吧,多个线程访问共享数据时,对吧?那么彼此的数据是可见的。对吧?在内存中是可见的对吧?我们说whatce,它用来干什么呢?说当多个线程进行操作共享数据时,对吧?内存中的保证可以保证,可以保证内存中的数据是可见的,对吧?是可见的原因是什么呢?我们说人家底层原理叫做是,实际上它调用的是计算机底层的代码,叫做内存栅栏,它实际上时时刻刻的把我们缓存中的数据是不是及时刷新过去啊,介绍新过去,或者说我们去怎么理解它呢?你就可以理解为他的操作就是在组存中完成的。
15:39
对吧,你说will态关键字修饰的这个变量,它操作是不是全都在主存中完成啊对吧?这里主存,然后这里有个flag初始值为false,对吧?然后呢,是不是依然是两个线程的对吧?这是一个线程一对吧,然后呢,还有一个main线程。
16:01
是不是这么个效果呀,对吧,然后这是线程一,线程一这头呢,是我们main线程对吧,Main线程main线程,Main线程负责读对吧,线程一是不是负责的写呀,对吧,负责的写。负责的写,那么一旦我用VO那字修饰了以后,对吧,我们说从底层是实际上是有一个内存栅栏调用底层代码是不是实时的给他们来个刷新的对吧?那当然我们的理解你就可以直接理解什么呢?就现在此时线程一和内线程他们的操作直接就操作主存中的数据。听懂了,直接就是对主存中的数据进行操作,也就是线程一当它把这个flag改了以后,对吧,那是不是就把原来的这个false变成了to啊,那么那线程在读的时候,那实际上是读到哪的,说每次都从主存中读,每次都从主存中中读吧,对吧?那可想而知,那我们说用了这个VO和不用all,那性能是不会下降啊,那是不是肯定会降低啊,你每次都操作主成能数据,每次都从你组织当中去读,对吧?那明显效率会降低一些。
17:22
相较于什么都不加的话,是这意思吧,但是它比锁的效率是不是就要高,是这意思吗?对吧?肯定是要比锁的效率是要高的,实际上沃勒太要关键字效率性能低,低在哪呢?低在于我们说实际上gdm的底层呢,它有个优化叫做重排序,对吧?所以说只不过用摩罗太修饰了以后,它不能重排序了而已。听懂吗?对吧,所以说我们说what ta呢,它能解决这个问题,也就是说刚才我说把所删掉了呀,这次呢,我只需要把这个共享变量啊,用这个voile对吧,Voile修饰一下以后注意看我是不是还来啊,右键运行。
18:06
搞定没有,是不是搞定了呀,右键再运行是不是搞定了呀,对吧,那么这就是will tell关键字的一个作用。能理解吧,对吧,就是保证内存可见性,保证多个线程访问共享数据,他们的内存中是可见的,数据是可见。能听懂吗?那那你就来了问咱那有了这个汪峰这么好对吧,那我们四试就是意味着不用新的关键册呢。那时候不是啊,我们说one time啊,它相较于single,它仅仅是一个可以有轻量级的一个同步策略,但是呢,它跟more,跟这个S呢,还是不一样的,注意不一样在哪呢?我们说它相较于相较于。相较于说,相较于呢,IC是RO新成麦子的呢,是一种较为较为轻量级的同步策略,但是呢,我们使用这个VO关键字的时候呢,还是要注意它是跟signineat是不是还是不一样的呀,不一样在哪呢?第一我们说这个waterile啊,它不具有不具备,不具备叫做互斥性,什么叫做互斥?
19:31
我们说singleize是不是就要称为互斥锁呀?也就意味着互斥锁指的就是当多个线程是不是同时强锁啊,一个一个线程抢到任锁,另外一个线程进得来吗?是不是进不来啊?什么时候我这个线程出去了以后,是不是另外一个才进得来啊?这时不是有这种叫互斥性呢?我们说snchize有互斥性,而we它就不具备这个互斥性是吧?也就是说如果有一个线程访问这个共享数据,另外一个线程你是不是依然可以访问对吧?只不过所有的访问都在主持中完成而已吧,对吧?那这是第一点,第二点说这个water tell还有什么特点呢?说water tell它叫做对于这个变量的原子性,对吧?说不能保证变量的叫做原子性,那这个问题就来了,原子性说什么是原子性,原子你们说啥意思,是不是不可分割的呀,对吧,我们说water呢,它不能。
20:32
保证原子性,所以说我tell是不是只是一种较轻量级的保证内存肯定性问题呀,但是呢,假如说你要是有这么几个需求的话,忘了特有关键字搞得定了呀,是不是就搞不定了呀,对吧?那么大家呢,对这个原子性呢比较陌生,过来咱们就针对原子性问题再进一步研究一下。
我来说两句