00:00
好,那么接下来我们上代码啊,大家看看这个代码应该是怎么去实现的,好吧,哎,这个呢,不太好理解各位啊。嗯。那行吧,我就在这儿吧。在这啊,Thread这块新建一个class,这个class叫thread test。呃,幺六。行吧,使用什么呀?Wait方法和什么呀,Notify方法。实现生产者和消费者模式。什么是生产者和消费者模式?这是个怎样的一个模式呢?就生产县城。对吧,负责生产消费,县城负责消费。
01:05
生产线程和消费线程要达到什么均衡?要达到均衡。这是一种特殊的什么业务需求?啊,业务需求在这种特殊的情况下需要使用什么呀,Wait方法和notify方法。第三,要知道wait和notify方法不是现成对象的方法啊,是普通Java对象都有的方法。第四个就是什么呢?就是wait方法和notify方法建立在线程同步的基础之上。啊,建立在线程同步的基础上,因为因为多线程要同时操作一个仓库。
02:05
有现成安全问题啊,有现场安全问题。然后要知道wait的方法呢,它会怎么做呢?Wait方法的作用啊,Wait方法作用。就是o.wait。让什么呀?让正在O对象上活动的线程T。进入什么,进入一个等待状态,并且释放掉什么,并且释放掉。T线程之前占有的O对象的所。有点绕各位啊,好好理解一下o.wait让正在O对向上。活动的线程T进入等待状态,并且释放掉T线程之前占有的O对象的锁。
03:05
Notify方法的作用o.notify。O点派啊,嗯,让让什么呀,正在O对象上等待的线程。唤醒。啊,只是通知不会释放O对向上。之前占有的锁。我们来写一个例子,来模拟一下这个生产者和消费者模式啊。比如说我们这个仓库就用什么呀,List集合吧,行吧。模拟啊,模拟这样一个需求。仓库呢?我们采用什么呀?例子的集合行吧,这个例子的集合中假设只能存储一个元素。
04:04
一个元素就表示什么,仓库满了。啊,如果,如果list集合中元素个数是零,就表示仓库空了。行吧,仓库我们采用list的集合,然后list集合中假设只能存储一个元素。各位啊,只能存一个元素,一个元素就表示仓库满了,如果绿色集合元素个数是零表示就表示仓库空了。就是永远保证啊,保证粒子的集合中永远都是最多存储什么一个元素。啊,就是生产一个消费一个,生产一个消费一个,就是必须做到这种效果,就是生产一个。消费一个啊,达到这种效果行吧,那行,那咱们就开始动手写吧,那这块呢,我定义一个什么呀,叫做生产线程。
05:09
对吧,负责生产嘛,一个是消费线程,两个线程,各位啊,那生产线程这一块的话,我们假如说这个class,我们叫producer。叫producer啊,Employs或者是extend都行吧,去实现这个roundable接口。好,然后去实现run方法。Run方法好了,那么接下来呢,这个这个消费线程呢,我们叫consumer吧,Consumer啊。Consumers去实现roundable,然后呢,接下来我们在这里呢,也同样去实现什么呀,一个方法,那么首先我们大家如果想让这两个线程共享同一个仓库的话,那我们是不是也应该给一个例子的呀?是不是应该给个例子的呀,各位,这是不是应该给个例子的。得共享啊,这个就是那个仓库吧,行吧,哎,仓库啊,然后接下来你这个仓库呢,这也应该有一个是同一个,应该都通过构造方法的方式给它传过来吧,所以这块呢,我们直接把producer的构造方法写一写producer,然后list集合,List集合参数呢直接传过来就行了,List list list。
06:19
好list好了,那么这边的代码呢,复制一下,然后直接扔过来放在下面就行了,行吧,然后这个呢,叫consumer。那consumer呢,是操作这个例子呢,我们的这个producer也是操作这个list。然后这个呢,我们在这怎么着啊,哎,在这儿一直什么一直生产吧,啊一直生产啊,一直生产,然后呢,在这个位置上呢,我们怎么着啊,一直什么一直消费吧,对不对,诶一直生产一直消费啊,但是我们必须要达到生产个消费均衡,生产一个消费一个,生产一个消费一个,就是这个线程执行了,另。另外线程等对吧,或者这个线程执行的时候,另外一个线程等对吧,哎,就达到这种效果啊,交替执行的这种效果啊,然后呢,这块呢,我们怎么去写呢?首先list的集合。
07:08
行吧,然后呢,里边呢,我也不用泛行了,各位啊,List你有一个什么list啊,有一个list表示这个集合里面什么都能存啊。用完之后呢,我们创建线程,这是创建仓库对象对吧,创建一个仓库对象共享的,那么这个时候我们创建两个线程,创建两个线程对象。一个是生产者线程,一个是消费者线程,生产者线程的话,那怎么怎么做呀,Th t new t1T行吧,哎,New一个什么呀,New一个thread new的时候呢,我们。New一个roundable就行了,是不是好大家看啊,New一个什么呀,Roundable,那new这个roundable的话,这个呢,就是producer呗,好了,Producer那这块呢,传一个什么呀,List进去好大,看new这个producer的时候,里边传个list呀。
08:01
对,我把这list不是进去了吗。那这个时候我把这个拿过来,诶,Thad t2等于new一个thad,然后呢,New的是一个random,这个random不是这个random啊,是下边这个random,这个是consumer consumer里也需要个list,那这个时候new一个consumer之后呢,里边也传一个什么样list进来。行吧,好,那么接下来我们t1.sitename吧,假如说给个名字啊,叫做生产者线程。行吧,哎,然后接下来T2.stem呢,我们就属于消费者线程呗。行吧,然后T一点什么呀,Start,然后T2点什么。T1启动,T2启动。这就行了,那最关键的问题就是这个生产怎么写,消费怎么写,来我们看看生产什么时候生产,而且还得一直生产,所以这个地方是个死循环吧,是不是可以使用死循环,使用死循环。来模拟什么一直什么生产行不行啊,那Y处呗,Y循环一直生产。
09:11
那生产的时候这块应该怎么做呢?再往下应该怎么写呢,各位?我们得先判断一下是吗?判断是什么?各位想一想,思考一下。如果说我们的这个例子集合的size。它要是大于零的话,这说明啥,各位。产品产品判断集合个数。如果list size,它大于零。我们应该干啥?还需要再生产吗?你好好思考一下,这一直生产一直生产,我们是生产一个消费一个吗。大于零就等等吧。
10:01
是不是谁等啊等吧,当前线程进入什么等待状态吧。是不是,哎,等待。因为你生产了,你大于零就代表你已经有了,你还生产干啥呀,你等啊对吧,等你调谁呀?调list集合它的一个方法叫。那么这个胃的方法会让谁进入等待状态?各位?会让谁进入等待状态?当前线程生产线程吧。大于零啊,大于零说明什么?大于零说明仓库中已经有一个元素了。当前线程进入等待状态,并且释放粒子的集合。的。
11:04
雨松老师,那锁没锁呀,我没看你锁在哪锁,哎,大家注意听啊,这个位的方法有异常,我们先处理一下行吧,TRY开始,你看TRY开始啊,TRY开好了,接下来我们执行一下,看看有没有问题走。目前没什么问题是吧,目前没什么问题啊,我们先把这个这个终止掉,大家看这是是不是进入上来就进入等待了。对吧,来进入等待了各位啊,那么这块呢,咱们需要需要在哪加锁,各位大家思考一下需要在什么地方加锁。思考一下啊。在哪里加锁?If下面。
12:02
如果list size大于零。加到这儿,各位。注意啊,Synchronized,为了保证安全,你把这块给它锁了。加到这啊,这一行代码是什么意思啊,就表示。给什么给仓库?对象list加锁啊,然后呢,程序走到这个位置上之后啊。它就相当于是当前的线程。进入等待状态,并且释放掉list集合的锁。注意啊,如果他要是不释放的话,他一直占着这个的集合这个锁的话,因为刚开始占住这个锁了呀。
13:02
对吧?哎,如果这块不释放这个锁的话,是不是这个程序就没有办法让另外一个线程执行了。消费线程是不是没法对这个集合进行操作?因为你持有这个对象锁呀,你现在是不是上来就Y处循环,把这个list的集合这个仓库就锁住了呀,如果仓库的size大于零的情况,你就wait呗,Wait就相当于让当前线程这producer生产线程进入等待状态,但是你之前这个producer这个线程是不是已经把这个历史的集合锁占有了,但现在在这儿啊,会释放掉这个锁。并且释放。释放这个producer之前占有的什么list集合的会释放掉。那会释放掉。来,那么接下来咱们继续,各位啊,这是生产。那么我们在这块呢,一直消费这个代码该怎么写,各位。
14:00
思考一下。这边同样也是用循环吧,各位。是不是上面一样啊。哎,然后这一块呢,紧接着我们是不是也得加锁呀。Synchronized list对不对?好,那么接下来呢,在这儿判断一下,如果说list集合它的size要怎么着,等于等于零的情况下,代表是不是仓库已经空了,仓库如果已经空了,那你还消费吗?是不是这个也得进去喂它。这个是不是也得加出开。你看这个代码实际上和上面的代码一样,只不过这个是大于零的情况下,我们这个wait呀。是不是如果等于零的情况下,我们是不是这个呀。
15:02
这个仓库已经空了,就不用再消费了。对吧,仓库空了,消费者线程等待释放掉。List集合的锁。那我问大家,如果这个条件不成立,我们回到这个地方啊,回到这个地方生产县城。生产线程啊,如果这个条件不成立。大家思考一下,程序能到能到这里,程序能够执行到这里,说明什么?因为要条件成立那个wait了。Wait就等待了呀,它程序能到这儿的话,这说明啥呀。这说明仓库是空的,可以生产吧?是不是,哎,Object我就生产一个new一个object出来了,那我接下来把list就叫爱的方法,把这个叫obj加进去。
16:13
对吧,大家思考一下来,我们输出一下啊,thread.current get这个线程。这是名字吧,哎,我们加一个什么呀,叫做OBJ。这个生产这这这是县城的名字吗。是吧,哎,生产县城它生产一个这样的对象。好,那我问大家。再往下,程序到这儿了,我们是不是需要去唤醒消费者?进行消费。
17:03
大家思考一下,在这里,因为你这里呢,你list它的size大于零的,这个情况下,就相当于仓库里边已经有东西了,那么这个时候呢。你的仓库里边已经有一个了。我是不是应该让我这个list集合wait,那这样的话,我这个produce这个线程它就怎么着就进入等待状态了吧,这就卡住了,卡死了吧,这卡到66行不不不动了吧。哎,不动了啊。但它会释放掉之前这个对象占的锁。明白所释放掉之后,大家想一想,多线程并发,你这个锁既然没了,那是不是就意味着我们另外这个Y要处循环可以继续执行?因为这S要拿这个对象锁呀。大家思考思考啊,来我再说一下,大家看看啊,很巧妙,就这个是我出循环,这个也是我出循环,大家知道我要出循环是死循环,这个也是死循环,这个是一直消费,这个是负责一直生产上来,我们给历史的枷锁是有目的的。
18:03
就是要你在执行这个同步代码块里边程序的时候,这个同步代块就不能执行,如果你执行这个同步代码块里边程序的时候,这个同步代码块就不能执行。听明白了吗?大家思考一下,想一想。只在同一个时间点上,只能有这个执行,或者说是这个执行,不能不可能存在这个,既执行这个又执行这个,不可能存在这种情况,因为这个仓库是共享的,它涉及到什么呀,线程安全问题,需要线程同步。那么在这块如果说你S要大于零的情况下,我们这边是不是就得wait一下,而这个wait注意了啊,这个wait只要一执行。我们这个线程就进入等待状态,等待状态,而且它还之前占的这个对象的锁,它就释放掉了,释放掉之后呢,接下来这个VR出去就就循环了呀。对吧,那这个Y处循环的话,大家想一想这个条件这个成立,这说明它集合里边有元素,有元素的话,这个执行的时候,这个条件就不会成立。
19:06
大家思考一下是不是是不是这个是不是这个意思,因为你大于零就等于你集合里边有一个元素,然后你为它之后,你释放了这个锁之后,你这个程序就可以继续执行,你这个程序继续执行的时候。你这块size等于零条件不成立,因为这是大于零的是有一个,所以在这里。应该怎么着啊?生产啊。消费吧,是不是消费啊。这个程序能够执行到此处。啊,说明仓库中有数据。进行消费。他不走这个呀,他会走这儿啊。
20:04
对吧,好,那么再我们反过来分析一下各位啊,如果说这个消费者线程先执行。那么这一块呢,我们会占有这个锁,占有锁之后呢,这个程序它就不能执行啊,然后接下来在这list size如果等于零的情况下,这个wait。Wait这个这个会不会释放这个对象的锁呀,会不会暂停卡住第一百一百行会卡住吧,是不是会卡住吧,哎卡住啊卡住各位卡住之后呢,接下来释放锁之后,那你这边的程序是不是就可以执行了。对吧,哎,这边程序可以执行。就这意思。当你这边程序执行的时候,它list size大于零,不可能成立,它会走这个。但是你要注意,你这个线程会调位的方法进入等待,你这个线程也会有一天会掉位的方法进入等待,假如说我现在在这个位置上,我的生产者这个线程进入了等待状态。
21:06
对吧,我进入等待了,我这儿一直等啊,一直卡在66行,那么接下来这边是不是消费啊。这边等的话,这边就是消费,同学们啊,消费就是执行这个程序。这这个同学的list集合。调什么呀,哪个方法各位。Remove吧。通过零下边来删行吗?删除的这个方法是不是有返回值?返回一个删除的对象吧,好,返回的就是删除的这个对象啊,来,我们输出一句话,thread.current thread.get name,然后加上去一个箭头,这个箭头呢,就是我们删的这个对象。然后接下来你想一想,因为你上面是卡住了,卡住了之后,你轮到了你这个线程去执行,你这个位置住的时候,我这个条件就不成立,会走这个。
22:04
那消费完之后,接下来你尴尬了呀,你如果再往下这块,你不唤醒的话,它就会有问题。对吧,哎,去唤醒什么。生产者生产。对吧,你这块应该是唤醒消费者进行消费。唤醒一下啊,怎么唤醒啊,Notify。那这块同样也是list什么。点notify,那有同学说了,老师这个list notify,他不会唤醒这个等待的吗?会。即使唤醒也无所谓,假如说你唤醒所有也没关系,知道为啥吗?来,各位告诉我,为什么唤醒所有也没关系。为什么?唤醒所有也没关系。
23:08
不是唤醒也没关系,是因为唤醒之后大家看是不是不会释放锁。他是不是还占着这个。嗯。其中一个必定会再次等待,其中一个必定会再次等待。注意这个在唤醒的时候,其实另外一个线程它就醒了。明白吧,就行了,各位啊,等他这个代码执行结束之后。我们这个对象的锁就释放掉了。
24:02
啊,等这个大框里边代码啊,这个大框大家看这个大框里代码执行结束之后,这个list的集和他占的那个锁就释放掉了。释放掉之后呢,这个线程就开始执行了。啊,就这个位就开始执行了,就唤醒了。明白吧,就这66行这个代码就唤醒了,就执行了,完了接着执行走了。我们先来看看能不能达到效果,各位啊,来先执行着,我们先看结果,再去说这个理论的事儿啊,来走一个。好,停停停停啊停来,我们回头看一下。哎呀,这是打印的一个问题啊,循循死循环,是不是生产者生产一个,你看消费者消费一个吧,生产者生产的内存地址,你看消费者是不是消费一个,生产者生产一个,消费者是不是消费一个,生产者生产一个,消费者消费一个不会出现那个生产一个又生产一个,不会出现,不会出现那个生产者生产一个,再生产一个,生产一个再生产一个是吧。
25:01
你看交替的吧,是不是达到这个交替效果,生产一个消费一个,生产一个消费一个,生产的对象内存地址和消费的内存地址是一样的。是吧,哎,大家看你看生产一个消费一个,生产一个消费一个,你去理解一下这个程序啊,大家看再看看这个程序能不能看懂,这程序不太好理解,就是wait和notify方法,它的一个使用。Notify all,唤醒所有。为的方法呢,是是什么呢?是让当前线程进入等待的状态啊,然后并且释放我们这个例子的集合上的锁,如果不释放这个锁的话,这个程序就彻底死到这儿了。你既占用了这个锁,然后呢,你又等待了状态下边程序又不能执行,不能执行这个对象的锁又不能释放,那你另外一个程序是不是就执行不了了呀。
26:06
所以这个为的方法是让当前生产线程进入等待的状态,并且会把粒子的集合上的这个锁给它立马释放掉。消费者释放了锁之后,可能会立马再次强锁吗?不会。不会。不会啊,这个notify方法,它的执行肯定会唤醒,马上这个就执行了。你这个代码执行结束之后。啊,你的意思是说,呃,这个看小鹏说什么意思啊,消费者释放了锁之后,可能立马再次强锁吗?
27:14
我们来看一看啊。呃,怎么能测一下这个。怎么能测一下这个。现在我们再来运行一下,这个时间是。没问题吧,死循环停一下,我们这里边有那种生产和消费这个错综在一起的。没有。是吧,生产消费生产消费生产消费生产消费。
28:03
他只要1NOTIFY之后呢,他这块呢,就。啊,有这个可能各位有这个可能抢到。这个notify方法是唤醒啊。唤醒等待的线程,唤醒他就在等的时候,他肯定没等,他在等的时候他肯定没等,他不可能两个同时都等,对吧?来我们先分析一下各位这两个线程,可能这两个。县城可能同时为他吗?对,有这个道理啊,沈聪说的对。
29:01
就算你抢到也没关系。你想到你还是等了。讲到你也没元素,没元素你还是等了啊。这两个线程不可能同时wait,各位啊,没关系,现在这个程序是可以正常执行的。就是我这块呢,List wait了,Wait之后呢,我这个应该是在这执行的,对吧,执行之后我notify之后,我这个锁释放了。锁释放了之后呢,你有可能你再强到锁,再强到锁没关系,因为你要等于零的话就wait了呀。有可能再抢到啊,有可能再抢到,这是有可能的。小鹏,收到了吗?来你看啊,这个wait方法在执行的时候,肯定是执行什么呀,这个程序。那么执行这个程序的时候,这个notify唤醒,唤醒之后呢,有可能就是这个代码执行结束之后怎么啊,刚才小鹏问的什么意思,就是说这个代码执行结束之后怎么着呢?他这个呃,锁不就释放了嘛,是吧,锁释放了之后呢,有可能这个代码开始执行了,对不对,那这个代码它也有可能开始执行,因为他要SNE去锁嘛,看谁先抢到,如果说这个是抢到这个锁。
30:12
这个抢到这个锁,那也就是有有这个可能,他如果抢到这个锁,它还是会wait的,他wait了之后呢,他这个锁释放掉,释放掉还是这个程序去执行,所以没关系。都等是不可能的,各位啊,两个都是wait是不可能的。你要么这个wait,要么是这个wait。当你这个wait的时候,这边肯定是在这个生产,当你这个wait的时候,这边肯定是在什么呀,哎,消费。肯定是在消费啊。行,那这个是生产者和消费者的一个模式,各位啊,就就采用这种方式,它的写法其实也比较固定啊,比较固定。好,那我现在问大家一个问题,这个地方没有新的,会不会有问题?
31:00
一次只能生产一个,可以生产多个呀。可以生产多个呀。生产十个行吗?各位,生产十个行不行?生产十个消费十个行吗?怎么做?如果list大于十。就等。如果list它的size等于零。就等。行不行啊。可以吧?那这个程序能不能执行,这边remove的话,下边只只写了一个零,能不能执行,我们来执行一下看看啊。
32:03
停。往上走啊找一段。呃,这个这个能看出来吗?生产了1234,然后消费者。啥情况?一二。看这个生产是怎么生产的,123456不对呀。各位,假如生产多个该怎么做?List size大于零的情况下,这是生产一个,消费一个。还得看一下仓库容量。大于零的情况下,等于零的情况下是吧。这个是没问题的,生产和消费你看是交替进行的。是不是,如果生产多个的话,大家改一下这个程序该怎么改,生产多个想一想。
我来说两句