多线程 生产者与消费者 遇到的问题以及解决方法

目录

最原始的生产者消费者模型

问题一 :产品product >= 1 时wait

问题二:增加一些消费者和生产者线程


最原始的生产者消费者模型

package com.juc;

/*
 * 生产者和消费者案例
 */
public class TestProductorAndConsumer1 {

	public static void main(String[] args) {
		Clerk clerk = new Clerk();
		
		Productor pro = new Productor(clerk);
		Consumer cus = new Consumer(clerk);
		
		new Thread(pro, "生产者 A").start();
		new Thread(cus, "消费者 B").start();
		
	/*	new Thread(pro, "生产者 C").start();
		new Thread(cus, "消费者 D").start();*/
	}
	
}

//店员
class Clerk{
	private int product = 0;
	
	//进货
	public synchronized void get(){
		if(product >= 20){
			System.out.println("产品已满!");
			
			try {
				this.wait();
			} catch (InterruptedException e) {
			}
			
		}else {

			System.out.println(Thread.currentThread().getName() + " : " + ++product);
			this.notifyAll();
		}
	}
	
	//卖货
	public synchronized void sale() {
		if (product <= 0) {
			System.out.println("缺货!");

			try {
				this.wait();
			} catch (InterruptedException e) {
			}
		} else {

			System.out.println(Thread.currentThread().getName() + " : " + --product);
			this.notifyAll();
		}
	}
}

//生产者
class Productor implements Runnable{
	private Clerk clerk;

	public Productor(Clerk clerk) {
		this.clerk = clerk;
	}

	@Override
	public void run() {
		for (int i = 0; i < 20; i++) {
			try {
				Thread.sleep(200);
			} catch (InterruptedException e) {
			}
			
			clerk.get();
		}
	}
}

//消费者
class Consumer implements Runnable{
	private Clerk clerk;

	public Consumer(Clerk clerk) {
		this.clerk = clerk;
	}

	@Override
	public void run() {
		for (int i = 0; i < 20; i++) {
			clerk.sale();
		}
	}
}

运算结果:

缺货!
生产者 A : 1
消费者 B : 0
缺货!
生产者 A : 1
消费者 B : 0
缺货!
生产者 A : 1
消费者 B : 0
缺货!
生产者 A : 1
消费者 B : 0
缺货!
生产者 A : 1
消费者 B : 0
缺货!
生产者 A : 1
消费者 B : 0
缺货!
生产者 A : 1
消费者 B : 0
缺货!
生产者 A : 1
消费者 B : 0
缺货!
生产者 A : 1
消费者 B : 0
缺货!
生产者 A : 1
消费者 B : 0
生产者 A : 1
生产者 A : 2
生产者 A : 3
生产者 A : 4
生产者 A : 5
生产者 A : 6
生产者 A : 7
生产者 A : 8
生产者 A : 9
生产者 A : 10

问题一 :产品product >= 1 时wait

  这个实例看着没有问题  如果把进货的  product >= 20 改为  product >= 1 运行程序会发现程序不会停止

修改后的运行结果:

缺货!
生产者 A : 1
消费者 B : 0
缺货!
生产者 A : 1
消费者 B : 0
缺货!
生产者 A : 1
消费者 B : 0
缺货!
生产者 A : 1
消费者 B : 0
缺货!
生产者 A : 1
消费者 B : 0
缺货!
生产者 A : 1
消费者 B : 0
缺货!
生产者 A : 1
消费者 B : 0
缺货!
生产者 A : 1
消费者 B : 0
缺货!
生产者 A : 1
消费者 B : 0
缺货!
生产者 A : 1
消费者 B : 0
生产者 A : 1
产品已满!

 

看最后三个结果:

消费者 B : 0
生产者 A : 1
产品已满!

    原因在于 :当消费者循环都运行完之后,消费者 B : 0 然后唤醒了生产者线程,之后 生产者 A : 1  唤醒了生产者和消费者线程,但是消费者线程已经执行完毕,最后只能执行生产者线程,由于产品已经满了所以只能wait不能执行下去 ,一直在等待着其他线程唤醒生产者线程,就出现了程序一直在等待。
    解决:去除else  

package com.juc;

/*
 * 生产者和消费者案例
 */
public class TestProductorAndConsumer1 {

	public static void main(String[] args) {
		Clerk clerk = new Clerk();
		
		Productor pro = new Productor(clerk);
		Consumer cus = new Consumer(clerk);
		
		new Thread(pro, "生产者 A").start();
		new Thread(cus, "消费者 B").start();
		
	/*	new Thread(pro, "生产者 C").start();
		new Thread(cus, "消费者 D").start();*/
	}
	
}

//店员

class Clerk{
	private int product = 0;
	
	//进货
	public synchronized void get(){//循环次数:0
		if(product >= 1){//为了避免虚假唤醒问题,
			System.out.println("产品已满!");
			
			try {
				this.wait();
			} catch (InterruptedException e) {
			}
			
		}
			System.out.println(Thread.currentThread().getName() + " : " + ++product);
			this.notifyAll();

	}
	
	//卖货
	public synchronized void sale() {//product = 0; 循环次数:0
		if (product <= 0) {
			System.out.println("缺货!");

			try {
				this.wait();
			} catch (InterruptedException e) {
			}
		}
			System.out.println(Thread.currentThread().getName() + " : " + --product);
			this.notifyAll();

	}
}

//生产者
class Productor implements Runnable{
	private Clerk clerk;

	public Productor(Clerk clerk) {
		this.clerk = clerk;
	}

	@Override
	public void run() {
		for (int i = 0; i < 20; i++) {
			try {
				Thread.sleep(200);
			} catch (InterruptedException e) {
			}
			
			clerk.get();
		}
	}
}

//消费者
class Consumer implements Runnable{
	private Clerk clerk;

	public Consumer(Clerk clerk) {
		this.clerk = clerk;
	}

	@Override
	public void run() {
		for (int i = 0; i < 20; i++) {
			clerk.sale();
		}
	}
}

结果:(一共出现20个缺货 以及20个消费者B)

缺货!
生产者 A : 1
消费者 B : 0
缺货!
生产者 A : 1
消费者 B : 0
缺货!
生产者 A : 1
消费者 B : 0
缺货!
生产者 A : 1
消费者 B : 0
缺货!
生产者 A : 1
消费者 B : 0
缺货!
生产者 A : 1
消费者 B : 0
缺货!
生产者 A : 1
消费者 B : 0
缺货!
生产者 A : 1
消费者 B : 0
缺货!
生产者 A : 1
消费者 B : 0
缺货!
生产者 A : 1
消费者 B : 0
缺货!
生产者 A : 1
消费者 B : 0
缺货!
生产者 A : 1
消费者 B : 0
缺货!
生产者 A : 1
消费者 B : 0
缺货!
生产者 A : 1
消费者 B : 0
缺货!
生产者 A : 1
消费者 B : 0
缺货!
生产者 A : 1
消费者 B : 0
缺货!
生产者 A : 1
消费者 B : 0
缺货!
生产者 A : 1
消费者 B : 0
缺货!
生产者 A : 1
消费者 B : 0
缺货!
生产者 A : 1
消费者 B : 0

缺货!
生产者 A : 1
消费者 B : 0

出现该结果的原因在于 去掉了else循环 所以每次循环都会打印缺货 然后线程wait  进而生产者生产产品 唤醒消费者线程 最后打印消费者

 

问题二:增加一些消费者和生产者线程

上面的情况适合一个消费者以及一个生产者情况 但是多几个生产者消费者会出现以下问题:

增加一个消费者和生产者:

public static void main(String[] args) {
		Clerk clerk = new Clerk();
		
		Productor pro = new Productor(clerk);
		Consumer cus = new Consumer(clerk);
		
		new Thread(pro, "生产者 A").start();
		new Thread(cus, "消费者 B").start();
		
		new Thread(pro, "生产者 C").start();
		new Thread(cus, "消费者 D").start();
	}
	

结果:

缺货!
缺货!
生产者 A : 1
消费者 D : 0
缺货!
消费者 B : -1
缺货!
生产者 C : 0
消费者 B : -1
缺货!
消费者 D : -2
缺货!
消费者 B : -3
缺货!
消费者 D : -4
缺货!
消费者 B : -5
缺货!
消费者 D : -6
缺货!
消费者 B : -7
缺货!
消费者 D : -8
缺货!
消费者 B : -9
缺货!
消费者 D : -10
缺货!
消费者 B : -11
缺货!
消费者 D : -12
缺货!
消费者 B : -13
缺货!
消费者 D : -14
缺货!
消费者 B : -15
缺货!
消费者 D : -16
缺货!
消费者 B : -17
缺货!
消费者 D : -18
缺货!
消费者 B : -19
缺货!
消费者 D : -20
缺货!
消费者 B : -21
缺货!
消费者 D : -22
缺货!
消费者 B : -23
缺货!
消费者 D : -24
缺货!
消费者 B : -25
缺货!
消费者 D : -26
缺货!
消费者 B : -27
缺货!
消费者 D : -28
缺货!
消费者 B : -29
缺货!
消费者 D : -30
缺货!
消费者 B : -31
缺货!
消费者 D : -32
缺货!
消费者 B : -33
缺货!
消费者 D : -34
缺货!
消费者 B : -35
缺货!
消费者 D : -36
缺货!
消费者 B : -37
消费者 D : -38
生产者 C : -37
生产者 A : -36
生产者 A : -35
生产者 C : -34
生产者 A : -33
生产者 C : -32
生产者 A : -31
生产者 C : -30
生产者 C : -29
生产者 A : -28
生产者 A : -27
生产者 C : -26
生产者 C : -25
生产者 A : -24
生产者 C : -23
生产者 A : -22
生产者 C : -21
生产者 A : -20
生产者 C : -19
生产者 A : -18
生产者 C : -17
生产者 A : -16
生产者 C : -15
生产者 A : -14
生产者 C : -13
生产者 A : -12
生产者 A : -11
生产者 C : -10
生产者 A : -9
生产者 C : -8
生产者 A : -7
生产者 C : -6
生产者 C : -5
生产者 A : -4
生产者 C : -3
生产者 A : -2
生产者 C : -1
生产者 A : 0

原因是因为:存在虚假唤醒

package com.juc;

/*
 * 生产者和消费者案例
 */
public class TestProductorAndConsumer1 {

	public static void main(String[] args) {
		Clerk clerk = new Clerk();
		
		Productor pro = new Productor(clerk);
		Consumer cus = new Consumer(clerk);
		
		new Thread(pro, "生产者 A").start();
		new Thread(cus, "消费者 B").start();
		
		new Thread(pro, "生产者 C").start();
		new Thread(cus, "消费者 D").start();
	}
	
}

//店员

class Clerk{
	private int product = 0;
	
	//进货
	public synchronized void get(){//循环次数:0
		while(product >= 1){//为了避免虚假唤醒问题,
			System.out.println("产品已满!");
			
			try {
				this.wait();
			} catch (InterruptedException e) {
			}
			
		}
			System.out.println(Thread.currentThread().getName() + " : " + ++product);
			this.notifyAll();

	}
	
	//卖货
	public synchronized void sale() {//product = 0; 循环次数:0
		while (product <= 0) {
			System.out.println("缺货!");

			try {
				this.wait();
			} catch (InterruptedException e) {
			}
		}
			System.out.println(Thread.currentThread().getName() + " : " + --product);
			this.notifyAll();

	}
}

//生产者
class Productor implements Runnable{
	private Clerk clerk;

	public Productor(Clerk clerk) {
		this.clerk = clerk;
	}

	@Override
	public void run() {
		for (int i = 0; i < 20; i++) {
			try {
				Thread.sleep(200);
			} catch (InterruptedException e) {
			}
			
			clerk.get();
		}
	}
}

//消费者
class Consumer implements Runnable{
	private Clerk clerk;

	public Consumer(Clerk clerk) {
		this.clerk = clerk;
	}

	@Override
	public void run() {
		for (int i = 0; i < 20; i++) {
			clerk.sale();
		}
	}
}

wait方法需要写在while循环中  不然存在虚假唤醒,当wait后被唤醒 还要去验证product是否大于1  然后再进行notifyAll。

 

 

发布了149 篇原创文章 · 获赞 23 · 访问量 12万+
展开阅读全文

java多线程,生产者消费者问题.

05-10

新手,在试着生产者消费者问题,刚开始的时候,SyncStack为空,但是会出现先执行c那个线程,打印出eat 0.然后才是produce: 0.jdk1.8的环境. 这个是为什么呀 ``` public class ProducerConsumer{ public static void main(String[] args){ SyncStack ss = new SyncStack(); Producer p = new Producer(ss); Consumer c = new Consumer(ss); new Thread(p).start(); new Thread(c).start(); // tp.start(); // tc.start(); } } class WoTou{ int id; WoTou(int id){ this.id = id; } public String toString(){ return "WoTou: "+id; } } class SyncStack{ int index = 0; WoTou[] arrWT = new WoTou[6]; public synchronized void push(WoTou wt){ if(index==arrWT.length){ try{ this.wait(); }catch(InterruptedException e){ e.printStackTrace(); } } this.notify(); arrWT[index] = wt; index++; } public synchronized WoTou pop(){ if(index == 0){ try{ this.wait(); }catch(InterruptedException e){ e.printStackTrace(); } } this.notify(); index--; return arrWT[index]; } } class Producer implements Runnable{ SyncStack ss = null; Producer(SyncStack ss){ this.ss = ss; } public void run(){ for(int i = 0;i<20;i++){ WoTou wt = new WoTou(i); ss.push(wt); System.out.println("produce: "+wt); try{ // Thread.sleep((int)(Math.random()*1000)); Thread.sleep(1000); }catch(InterruptedException e){ e.printStackTrace(); } } } } class Consumer implements Runnable{ SyncStack ss = null; Consumer(SyncStack ss){ this.ss = ss; } public void run(){ for(int i = 0;i<20;i++){ WoTou wt = ss.pop(); System.out.println("eat: "+wt); try{ // Thread.sleep((int)(Math.random()*1000)); Thread.sleep(100); }catch(InterruptedException e){ e.printStackTrace(); } } } } ``` 问答

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 像素格子 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览