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

目录

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

问题一 :产品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

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

多线程 生产者与消费者 遇到的问题以及解决方法_第1张图片

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。

 

 

你可能感兴趣的:(多线程,JUC,多线程,生产者消费者)