美文网首页
java多线程协作

java多线程协作

作者: X猪 | 来源:发表于2018-08-03 22:51 被阅读19次

本文用一个生活中的例子(订阅-投送报纸)阐明多线程协作的问题。在java中,就是 wait/notify/synchronized的相关用法。

附带的代码既是一个测试案例,同时也是一个简单的“生产者-消费者”框架。如果使用此框架,用户只需实现自己的 生产过程、消费过程、产品的取放,框架代码已经实现 生产-消费 线程间的协作,参考代码中test包。地址:生产-消费 多线程代码

线程间协作

多个线程可以协作完成一项任务,比如典型的“生产-消费”问题,生产者线程负责创建一些产品(数据),消费者负责使用这些产品(数据)。

多线程协作可以保持生产者和消费者互相独立,各做各的事情,生产者只管生产,不管谁来消费以及如何消费,消费者也不管是谁来生产产品。这使得程序逻辑清晰简洁。

不过,无法避免的接触就是它们两者之间传递的产品,消费者必须确认产品已经生产出来才能开始消费,生产者通常也必须确认有足够的仓库容量来存放新生产的产品。因此必须采取某种机制来保证他们的工作不会出现混乱。

订阅和投送报纸的例子(“生产-消费”问题)

我们用订阅和投送报纸的例子来考虑这个问题。A订阅了一份报纸,邮递员B负责投递,但投递时间是不一定的。A希望尽快的拿到报纸,最好是B一完成投递就拿到报纸阅读。简单粗暴的方式是A反复的检查邮箱,看到报纸就拿走。但这样太辛苦了,绝大多数时候都是无效劳动。用计算机程序来做的话就是不断查询邮箱状态,很浪费CPU时间。

门铃(notifyAll, notify)

所以比较好的方法是安装一个专用门铃(专用的意思是只要铃响就意味着邮箱中肯定有报纸),A只需在房间休息(wait),当B投递报纸后按一下门铃(notifyAll, notify),A就知道出来拿报纸了。

但是可能出现两种问题。

一个是虚假门铃(spurious wakeups)。比如邻居小孩有点淘气,有时候会去乱按门铃。A这时出来就拿不到报纸。另一个是共用门铃(多个生产线程;多个消费线程;多种唤醒条件)。比如A家里夫妻两个(多个消费线程)都准备去拿报纸,门铃响了以后,A1去拿回报纸,A2再去就只是空邮箱了。

结果就是非专用门铃(即无法确保门铃是专用的,铃响不代表邮箱中一定有报纸)。不过解决办法也很简单,就是检查后再行动("check and act"),每次门铃响了以后,先检查邮箱,如果有报纸再拿回来,如果没有就继续回去休息。

时间差问题(多线程交错运行)

但是,检查后再行动引起了时间差问题。对于人来说打开邮箱拿报纸似乎是一件事情,但如果事先并不确定里面是否有报纸的话,检查 和 行动其实是两个步骤。时间差问题,比如这样的情况:A出来检查邮箱,发现没有报纸,于是决定回去休息,往回走的路上,邮递员到了,邮递员把报纸放进邮箱,按了门铃,但这时候A还在回房间的路上,并没有听见门铃响,也就不知道邮递员已经送来报纸,于是A按预定计划回房间休息,而邮递员已经走了,结果A错过了这份报纸。

如何解决呢?我们要确保A查看邮箱到回到房间这段时间内,邮递员不能进行投递。所以可以修建一个保护罩(synchronized),将邮箱和门口的道路("check and act")都笼罩在内,而保护罩同一时间只允许A或B之中的一个人进入。比如上面的情况,A进入保护罩查看邮箱,发现没有报纸后回去休息,这时邮递员B到达准备投递,但A还在保护罩里面,禁止B进入,B只好在外面等一下。等A离开保护罩回到房间,这时邮递员才能进入保护罩并投递报纸,然后按下门铃,而这时A已经在房间里了,肯定能听到铃声,就会出来拿报纸了。

小结

在java程序中,A休息用 wait() 实现,B按门铃用 notifyAll() 或 notify() 实现,邮箱的状态(有没有报纸)可以通过定义一个变量来表示,A检查该变量的值来决定是拿报纸还是回去休息。要使用 while循环检查变量状态("check and act")来避免虚假门铃和共用门铃的问题。保护罩用 synchronized关键字 实现,确保("check and act")成为一个原子操作。

相关文章

  • 带你搞懂Java多线程(四)

    带你搞懂Java多线程(一)带你搞懂Java多线程(二)带你搞懂Java多线程(三) 什么是线程间的协作 线程之间...

  • java多线程协作

    本文用一个生活中的例子(订阅-投送报纸)阐明多线程协作的问题。在java中,就是 wait/notify/sync...

  • 线程工具类之CountDownLatch、CyclicBarri

    CountDownLatch、CyclicBarrier是JAVA提供的多线程工具类,用于协调线程之间协作的。里面...

  • JAVA高并发(四)

    线程间的协作: 多线程的世界线程并不孤立,线程间常见的协作方式以及java提供的支持: 最为经典的便是Obje...

  • Java多线程之并发协作生产者消费者设计模式JDK1.5.0+升

    上一篇[Java多线程之并发协作生产者消费者设计模式]已经讲述了在Java的多线程中,如何处理并发安全的生产者消费...

  • java 中线程协作

    线程协作 多线程协作的典型场景,生产者消费者模型。JAVA 中提供的线程阻塞和线程唤醒 Api 被弃用的 susp...

  • Java并发之JDK并发包(1)

    实战Java高并发程序设计笔记 多线程的团队协作:同步控制 synchronied的功能扩展:重入锁 简单使用,与...

  • 4.线程通信

    线程协作-JDK API JDK中对于需要多线程协作完成某一任务的场景,提供了对应API支持。多线程协作的典型场景...

  • 多线程笔记1-线程的共享与协作

    什么是多线程的共享? 什么是多线程之间的协作? 多线程的共享:是指多个线程访问同一个对象。 多线程的协作是指:当A...

  • 带你搞懂Java多线程(五)

    带你搞懂Java多线程(一)带你搞懂Java多线程(二)带你搞懂Java多线程(三)带你搞懂Java多线程(四) ...

网友评论

      本文标题:java多线程协作

      本文链接:https://www.haomeiwen.com/subject/kmfpvftx.html