黑马程序员技术交流社区

标题: 请大神详细解释下多线程中生产者与消费者的通信问题 [打印本页]

作者: Kris    时间: 2015-10-27 23:31
标题: 请大神详细解释下多线程中生产者与消费者的通信问题
这是多线程通信中的难点问题吧,看的时候总是有些稀里糊涂的,对数据的同步处理不好。。。


作者: li3N    时间: 2015-10-28 14:31
是毕老师的视频吧,我刚看的时候也是一头雾水,后来去网上查了些资料好多了。
按照毕老师的意思就是把生产者和消费者线程共享的资源定义成一个类,具体的资源就是这个类中的一个或几个私有成员变量。资源类为这些成员变量提供同步的set和get方法(也就是说这两个方法都是同步函数)去访问。生产者线程通过调用set方法向资源中写东西,消费者线程通过调用get方法拿到资源中的东西然后消费。
以上是大致的框架。要实现生产者和消费者交替执行,就在资源类中再加一个私有的成员变量,叫做标志位(如果是生产者和消费者这种问题的话,标志位用boolean类型就可以了,如果是3个或以上线程要循环交替执行的话,标志位就要用int类型),并在set和get方法代码的开头加上对标志位的判断,结尾加上对标志位的改变,并通过同步锁(也就是this)对生产者和消费者线程进行冻结或唤醒操作(在开头,如果不符合条件就冻结持有锁的线程;在结尾,改变标志位以后,唤醒对方线程)。换种说法就是同步锁对象是总指挥,标志位是指挥棒,而生产者和消费者是被指挥的人。
举个顾客去饭店吃饭的例子。假设饭店只有一张桌子,厨师负责做饭往这张桌子上端,顾客负责把这张桌子上的饭吃掉。这个例子中桌子就是资源类,桌子上的饭就是资源,厨师就是生产者线程,而顾客就是消费者线程。
如果只有一个厨师和一个顾客,那么吃饭的过程就是这样的:
厨师:把做好的饭端到桌子上-->告诉顾客可以吃饭了-->回到厨房等待顾客通知-->听到顾客通知后,再把做好的饭端到桌子上,如此循环。
顾客:等待厨师端饭上来-->听到厨师说可以吃饭以后,把饭吃掉-->通知厨师再做饭-->等待厨师端饭上来,如此循环。
桌子上有没有饭就是一个标志位,可以认为这个标志位是属于桌子(资源类)的。一开始桌子上没有饭(标志位为假),顾客就会通知在厨房等待的厨师做饭,自己在桌子前等待(消费者线程唤醒生产者线程,然后自身进入冻结状态);厨师收到顾客通知后就开始做饭(生产者的执行动作),做好后端上来(把标志位改成真),然后通知顾客可以吃饭了,自己回去厨房继续等顾客通知(生产者线程唤醒消费者线程,然后自身进入冻结状态);顾客听到厨师通知可以吃饭后就开吃(消费者的执行动作),吃完以后再通知厨师做饭(把标志位改成假),如此循环。
如果有多个厨师和多个顾客,那么吃饭的过程就是这样的:
厨师方:一个厨师把做好的饭端到桌子上-->告诉所有的顾客可以吃饭了-->回到厨房等待顾客通知-->听到顾客通知后,再由抢到做饭机会(姑且认为所有的厨师都背负着很大的养家压力,所以做饭机会是要抢的)的厨师把做好的饭端到桌子上,如此循环。
顾客:等待一个厨师端饭上来-->听到厨师说可以吃饭以后,先抢到饭的那个顾客把饭吃掉-->通知所有的厨师再做饭-->等待一个厨师端饭上来,如此循环。
这种情况和上面一个厨师和一个顾客的情况的区别有两点:
1.端饭的厨师通知的是所有顾客,吃完饭的顾客通知的是所有厨师——唤醒机制由notify改为notifyAll;
2.在厨师做饭的过程中,所有的顾客都会时不时地往桌子上看一眼,因为他们要抢一碗饭;在顾客吃饭的过程中,所有的厨师都会时不时地往桌子上看一眼,因为他们要抢一个做饭机会(姑且认为所有的厨师都背负着很大的养家压力)——等待机制由一次判断(if)改为循环判断(while)。
虽然这么打比方和实际的程序运行有些区别,但勉强解释应该是够了,欢迎楼下大神拍砖!

作者: Kris    时间: 2015-10-28 23:36
li3N 发表于 2015-10-28 14:31
是毕老师的视频吧,我刚看的时候也是一头雾水,后来去网上查了些资料好多了。
按照毕老师的意思就是把生产 ...

感谢详细耐心的回答~
视频中的 案例仔细看并自己敲了几遍基本搞清楚了,实际开发中应该线程更多,情况更复杂。。
作者: T-l-H、小生    时间: 2015-10-29 00:58
自从搞了安卓,这些东西早就忘记得七七八八了~~~看着二楼的回复,复习一下。。。




欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) 黑马程序员IT技术论坛 X3.2