Java_线程—经典的例子:生产者和消费者

线程,还有一个经常出现的例子:生产者和消费者。

一个生产,一个消费,中间就是产品,一个产品只能被一个生产者生产,同样,也只能被一个消费者消费。

既然是生产和消费,这就好理解了,现在也快中秋了,举个例子:生产月饼的厂家和消费者。厂家负责生产,消费者进行消费。厂家不断的生产,消费者就不断的消费,这有个前提,厂家生产月饼在前,消费者消费月饼在后,不能说消费者先消费月饼,然后厂家再去生产吧,这明显就不靠谱。还有一点,厂家生产莲蓉蛋黄口味的月饼,消费者是不是只能消费莲蓉口味的月饼,而不能消费五仁口味的月饼,对吧。这点搞清楚了,我们就来写个小程序:


一气呵成,全部代码都在这里了。整个代码也很好理解。

line14-16是一个月饼类,月饼只定义了一个属性,那就是口味,kind。

line18-46是生产月饼的线程,线程需要传递进去一个月饼,然后对月饼进行生产。其中,根据不同生产的编号,相应生产不同口味的月饼。

line28-66是消费月饼的线程,消费月饼也需要传递一个月饼过去。消费很简单,就打印出月饼的口味。

最后是main函数,new一个月饼对象,然后一个生产月饼的线程和一个消费月饼的线程,然后启动。

看完代码来看看运行的结果。


代码运行的结果如上,为什么要给出两个结果呢,因为线程的执行是不是具有不确定性?你能确定是哪个线程抢到CPU的执行权吗?对吧。<如果你尝试着写完后运行总是结果一的话,那么你多运行几次,或者你把怒骂改大一点,就比较明显。>

先来看结果一,结果一一次把全部月饼都给生产完了,然后再进行消费,这点是没错的,先生产后消费嘛。但你仔细看看,生产是没错的,分别生产不同口味的月饼,但看消费,消费就奇怪了,5次消费的都是同一种口味的月饼,这就很明显不对了嘛。(如果你给月饼加个编号属性的话,你就会发现,消费的都是最后生产的那个月饼)

再来看看结果二,结果二中,两个线程就有点交叉了,但你仔细一看,还是不对,消费的时候还是出错了。


这问题该怎么解决呢?

同步,加锁。生产的时候,如果月饼是生产好的,那么就等待,等消费者消费完后再生产,同时,生产完毕后要通知消费者去消费,同样的道理,消费者消费的时候先看一下这月饼是不是 已经生产好了,如果生产好了就消费,同时,消费完毕后通知生产者继续生产下一个产品。


下面对上面的小例子进行改进。

看代码:


代码不详细解释了,只解释一下改动的部分,

其中,月饼类,加多了一个标记,标记月饼是否已经生产好。

其次,生产月饼的线程和消费月饼的线程加了同步和锁,代码中也有相应的解释,这里就不再重复了。

最后看打印的结果,是不是老老实实的按照我们原来定的规则进行生产和消费呢?

别不信,不信你可以多运行几次这段代码,看看结果是否一致。


既然都按照我们的要求生产了,这不就完事了么,别,还没完。发现没有,这代码有严重的缺陷,就是我们生产和消费的线程里面加锁,那么我们每次写线程的时候是不是都要加锁呢?我们能不能够在月饼类里面定义一个生产和消费的方法,把锁和同步加到月饼类里面去呢?这样是不是比较方便呢?不用每次生产和每次消费的时候都去加锁,而且还有一个缺点是:如果我生产和消费的时候加的不是同一把锁会出现什么情况呢?对吧,这些都是需要考虑的。

下面对上面这段代码进行优化。


这是优化后的代码,你会发现,锁没了,同步代码块也好像没了,但方法却多了个同步,对,就是这样。我们在生产和消费的方法中加了同步,其他还是一样,生产前先判断是否已经生产好,生产完毕后改标记后通知消费者消费,同理,消费者也一样,先判断是否已经有了,然后再消费,消费完后通知生产者生产。最后生产和消费的线程是不是变得很简单,只需要调用月饼类的生产和消费的方法即可;


代码到这里看起来好像要结束了。

不不不,还没结束,你发现没,同步是我们自己写的,java里面有没有已经写好的呢?对吧,如果有的话,就直接用呗,还需要那么麻烦自己去写么?还有一点,你发现没,我最后notify,其实是通知了全部线程,也就是说,不论是生产还是消费的线程都通知了,我们能不能够做到,生产完只通知消费者,消费完只通知生产者呢?

下面来看最终的代码:


代码只是月饼类做了改动,其他都没动,还是原来的样子。现在来说说代码的不同之处;

line22,new了一个锁,line23-24,分别new了两个生产和消费的状态。

生产:生产的时候先锁上,然后判断是否已经有,如果有,那么生产的Condition等待,如果没有就进入生产,生产完后通知消费者消费,最后释放锁。

消费:消费的时候先锁上,然后判断是否已经有,如果没有就等待,如果有就消费,消费完后通知生产者生产。

这里要注意的是:引入Lock和Condition的时候。等待是await,而不是wait,通知不再是notify,而是signal。



好了,线程篇先写到这里,后续再补充其他吧!

线程篇:

Java_线程-Thread

Java_线程—经典的例子:售票

Java_线程—经典的例子:生产者和消费者


java_多线程下载

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值