Java多线程之并发协作生产者消费者设计模式

题图

两个线程一个生产者个一个消费者

需求情景

涉及问题

代码实现(共三个类和一个main方法的测试类)

多个线程,多个生产者和多个消费者的问题

需求情景

涉及问题

再次测试代码

代码改进(Resource中的if->while)

最后代码改进(Resource中的notify()->notifyAll())

————————————————————————————————

两个线程一个生产者个一个消费者

需求情景

两个线程,一个负责生产,一个负责消费,生产者生产一个,消费者消费一个

涉及问题

同步问题:如何保证同一资源被多个线程并发访问时的完整性。常用的同步方法是采用标记或加锁机制

wait() / nofity() 方法是基类Object的两个方法,也就意味着所有Java类都会拥有这两个方法,这样,我们就可以为任何对象实现同步机制。

wait()方法:当缓冲区已满/空时,生产者/消费者线程停止自己的执行,放弃锁,使自己处于等等状态,让其他线程执行。

notify()方法:当生产者/消费者向缓冲区放入/取出一个产品时,向其他等待的线程发出可执行的通知,同时放弃锁,使自己处于等待状态。

代码实现(共三个类和一个main方法的测试类)

Resource.java

/**

* Created by yuandl on 2016-10-11./**

* 资源

*/public class Resource {

/*资源序号*/

private int number = 0;

/*资源标记*/

private boolean flag = false;

/**

* 生产资源

*/

public synchronized void create() {

if (flag) {//先判断标记是否已经生产了,如果已经生产,等待消费;

try {

wait();//让生产线程等待

} catch (InterruptedException e) {

e.printStackTrace();

}

}

number++;//生产一个

System.out.println(Thread.currentThread().getName() + "生产者------------" + number);

flag = true;//将资源标记为已经生产

notify();//唤醒在等待操作资源的线程(队列)

}

/**

* 消费资源

*/

public synchronized void destroy() {

if (!flag) {

try {

wait();

} catch (InterruptedException e) {

e.printStackTrace();

}

}

System.out.println(Thread.currentThread().getName() + "消费者****" + number);

flag = false;

notify();

}

}

Producer.java

/**

* Created by yuandl on 2016-10-11.

*

/**

* 生产者

*/public class Producer implements Runnable {

private Resource resource;

public Producer(Resource resource) {

this.resource = resource;

}

@Override

public void run() {

while (true) {

try {

Thread.sleep(10);

} catch (InterruptedException e) {

e.printStackTrace();

}

resource.create();

}

}

}

Consumer.java

/**

* 消费者

*/

public class Consumer implements Runnable {

private Resource resource;

public Consumer(Resource resource) {

this.resource = resource;

}

@Override

public void run() {

while (true) {

try {

Thread.sleep(10);

} catch (InterruptedException e) {

e.printStackTrace();

}

resource.destroy();

}

}

}

ProducerConsumerTest.java

/**

* Created by yuandl on 2016-10-11.

*/

public class ProducerConsumerTest {

public static void main(String args[]) {

Resource resource = new Resource();

new Thread(new Producer(resource)).start();//生产者线程

new Thread(new Consumer(resource)).start();//消费者线程

}

}

打印结果

Thread-0生产者------------1

Thread-1消费者****1

Thread-0生产者------------2

Thread-1消费者****2

Thread-0生产者------------3

Thread-1消费者****3

Thread-0生产者------------4

Thread-1消费者****4

Thread-0生产者------------5

Thread-1消费者****5

Thread-0生产者------------6

Thread-1消费者****6

Thread-0生产者------------7

Thread-1消费者****7

Thread-0生产者------------8

Thread-1消费者****8

Thread-0生产者------------9

Thread-1消费者****9

Thread-0生产者------------10

Thread-1消费者****10

以上打印结果可以看出没有任何问题

————————————————————————————————————————

多个线程,多个生产者和多个消费者的问题

需求情景

四个线程,两个个负责生产,两个个负责消费,生产者生产一个,消费者消费一个。

涉及问题

notifyAll()方法:当生产者/消费者向缓冲区放入/取出一个产品时,向其他等待的所有线程发出可执行的通知,同时放弃锁,使自己处于等待状态。

再次测试代码

ProducerConsumerTest.java

/**

* Created by yuandl on 2016-10-11.

*/

public class ProducerConsumerTest {

public static void main(String args[]) {

Resource resource = new Resource();

new Thread(new Consumer(resource)).start();//生产者线程

new Thread(new Consumer(resource)).start();//生产者线程

new Thread(new Producer(resource)).start();//消费者线程

new Thread(new Producer(resource)).start();//消费者线程

}

}

运行结果

Thread-0生产者------------100

Thread-3消费者****100

Thread-0生产者------------101

Thread-3消费者****101

Thread-2消费者****101

Thread-1生产者------------102

Thread-3消费者****102

Thread-0生产者------------103

Thread-2消费者****103

Thread-1生产者------------104

Thread-3消费者****104

Thread-1生产者------------105

Thread-0生产者------------106

Thread-2消费者****106

Thread-1生产者------------107

Thread-3消费者****107

Thread-0生产者------------108

Thread-2消费者****108

Thread-0生产者------------109

Thread-2消费者****109

Thread-1生产者------------110

Thread-3消费者****110

通过以上打印结果发现问题

101生产了一次,消费了两次

105生产了,而没有消费

原因分析

当两个线程同时操作生产者生产或者消费者消费时,如果有生产者或者的两个线程都wait()时,再次notify(),由于其中一个线程已经改变了标记而另外一个线程再次往下直接执行的时候没有判断标记而导致的。

if判断标记,只有一次,会导致不该运行的线程运行了。出现了数据错误的情况。

解决方案

while判断标记,解决了线程获取执行权后,是否要运行!也就是每次wait()后再notify()时先再次判断标记

代码改进(Resource中的if->while)

Resource.java

/**

* Created by yuandl on 2016-10-11./**

* 资源

*/public class Resource {

/*资源序号*/

private int number = 0;

/*资源标记*/

private boolean flag = false;

/**

* 生产资源

*/

public synchronized void create() {

while (flag) {//先判断标记是否已经生产了,如果已经生产,等待消费;

try {

wait();//让生产线程等待

} catch (InterruptedException e) {

e.printStackTrace();

}

}

number++;//生产一个

System.out.println(Thread.currentThread().getName() + "生产者------------" + number);

flag = true;//将资源标记为已经生产

notify();//唤醒在等待操作资源的线程(队列)

}

/**

* 消费资源

*/

public synchronized void destroy() {

while (!flag) {

try {

wait();

} catch (InterruptedException e) {

e.printStackTrace();

}

}

System.out.println(Thread.currentThread().getName() + "消费者****" + number);

flag = false;

notify();

}

}

运行结果


再次发现问题

打印到某个值比如生产完74,程序运行卡死了,好像锁死了一样。

原因分析

notify:只能唤醒一个线程,如果本方唤醒了本方,没有意义。而且while判断标记+notify会导致"死锁"。

解决方案

notifyAll解决了本方线程一定会唤醒对方线程的问题。

最后代码改进(Resource中的notify()->notifyAll())

Resource.java

/**

* Created by yuandl on 2016-10-11./**

* 资源

*/public class Resource {

/*资源序号*/

private int number = 0;

/*资源标记*/

private boolean flag = false;

/**

* 生产资源

*/

public synchronized void create() {

while (flag) {//先判断标记是否已经生产了,如果已经生产,等待消费;

try {

wait();//让生产线程等待

} catch (InterruptedException e) {

e.printStackTrace();

}

}

number++;//生产一个

System.out.println(Thread.currentThread().getName() + "生产者------------" + number);

flag = true;//将资源标记为已经生产

notifyAll();//唤醒在等待操作资源的线程(队列)

}

/**

* 消费资源

*/

public synchronized void destroy() {

while (!flag) {

try {

wait();

} catch (InterruptedException e) {

e.printStackTrace();

}

}

System.out.println(Thread.currentThread().getName() + "消费者****" + number);

flag = false;

notifyAll();

}

}

运行结果

Thread-0生产者------------412

Thread-2消费者****412

Thread-0生产者------------413

Thread-3消费者****413

Thread-1生产者------------414

Thread-2消费者****414

Thread-1生产者------------415

Thread-2消费者****415

Thread-0生产者------------416

Thread-3消费者****416

Thread-1生产者------------417

Thread-3消费者****417

Thread-0生产者------------418

Thread-2消费者****418

Thread-0生产者------------419

Thread-3消费者****419

Thread-1生产者------------420

Thread-2消费者****420

以上就大功告成了,没有任何问题

来源:http://blog.csdn.net/linglongxin24/article/details/52788774

—————————————————————————————————

在阅读文章过程中如有疑问,请发布到极乐网;或者长按下方二维码,关注极官方微信平台,在平台下方留言,我们将第一时间为您解答。

极乐技术问答
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容