线程的生产者和消费者模型

生产者和消费者模型:

该代码模型的目的就是让我们对多线程更加深入的理解,所谓的生产者就是负责生产对象的一个线程(生产者线程),消费者就是负责消费(取出对象)生产者生产的对象的另外一个线程(消费者线程)。

创建一个Animal类:

package com.sun.test;
import java.io.Serializable;
class Animal implements Serializable {
	private String name;// 动物的名字
	private String desc;// 动物的描述
	/**
	 * 设置名字和描述 该方法是生产者调用
	 * @param name
	 * @param desc
	 */
	public void set(String name, String desc) {
		this.name = name;
		try {
			Thread.sleep(100);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		this.desc = desc;
	}

	/**
	 * 消费者线程调用的方法
	 */
	public void get() {
		try {
			Thread.sleep(100);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println(this.name + "-->" + this.desc);
	}
}

// 定义生产者线程主体类
class Productor implements Runnable {
	private Animal animal;
	public Productor() {
	}
	public Productor(Animal animal) {
		this.animal = animal;
	}
	@Override
	public void run() {
		for (int i = 0; i < 50; i++) {
			if (i % 2 == 0) {
				this.animal.set("小明", "很帅的一个男孩");
			} else {
				this.animal.set("二哈", "拆家的二哈");
			}
		}
	}
}
// 定义消费者线程
class Customer implements Runnable {
	private  Animal  animal;
	public Customer() {
	}
	public Customer(Animal  animal) {
		this.animal=animal;
	}
	@Override
	public void run() {
		for(int i=0;i<50;i++) {
			this.animal.get();
		}
	}
}
public class Test {
	public static void main(String[] args) {
		 //创建一个动物对象
		 Animal  animal=new Animal();
		 //创建生产者线程对象
		 new Thread(new Productor(animal)).start();;
 		 //创建消费者线程对象
		 new Thread(new Customer(animal)).start();
	}
}

在这里插入图片描述
此时出现数据错位的现象,其实上生产者线程在不断更新动物对象的名字和描述,当更新数据的时候还没完全将两个信息更新完毕,消费者线程就把对象给取走了,所以出现了数据错位的现象。出现这个问题的根本原因是线程没有同步导致,此时我们可以让set和get变成同步方法。

线程同步解决问题:

package com.sun.test;

import java.io.Serializable;

class Animal implements Serializable {
	private String name;// 动物的名字
	private String desc;// 动物的描述

	/**
	 * 设置名字和描述 该方法是生产者调用
	 * 
	 * @param name
	 * @param desc
	 */
	public synchronized void set(String name, String desc) {
		this.name = name;
		try {
			Thread.sleep(100);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		this.desc = desc;
	}
	/**
	 * 消费者线程调用的方法
	 */
	public synchronized void get() {
		try {
			Thread.sleep(100);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println(this.name + "-->" + this.desc);
	}
}

// 定义生产者线程主体类
class Productor implements Runnable {
	private Animal animal;
	public Productor() {
	}
	public Productor(Animal animal) {
		this.animal = animal;
	}
	@Override
	public void run() {
		for (int i = 0; i < 50; i++) {
			if (i % 2 == 0) {
				this.animal.set("小明", "很帅的一个男孩");
			} else {
				this.animal.set("二哈", "拆家的二哈");
			}
		}
	}
}
// 定义消费者线程
class Customer implements Runnable {
	private  Animal  animal;
	public Customer() {
	}
	public Customer(Animal  animal) {
		this.animal=animal;
	}
	@Override
	public void run() {
		for(int i=0;i<50;i++) {
			this.animal.get();
		}
	}
}
public class Test {
	public static void main(String[] args) {
		 //创建一个动物对象
		 Animal  animal=new Animal();
		 //创建生产者线程对象
		 new Thread(new Productor(animal)).start();;
 		 //创建消费者线程对象
		 new Thread(new Customer(animal)).start();
	}
}

在这里插入图片描述
此时使用同步方法锁解决了数据错位的现象但是又出现了数据重复的问题,这个问题你自己分析根本原因。

解决数据重复问题:

最好的解决方案是生产者生产出一个对象之后通知消费者可以取走对象,在生产期间消费者线程要等待。等待消费者取走对象之后接收到消费者的通知之后再生产(消费者取走对象之后需要通知生产者可以生产了),在消费者取得对象期间生产者要等待,这样重复循环操作,就不会出现数据重复的现象。要实现上面的操作需要让两个线程之间有通讯信息,实现线程通讯的基本方法是Object类提供的方法:

public final void wait() throws InterruptedException
【让线程休眠,但是不会自动醒来,需要唤醒(使用sleep方法休眠的线程可以自动醒来)】

Causes the current thread to wait until 
another thread invokes the notify() method or 
the notifyAll()method for this object

public final void notify()
【唤醒第一个使用wait()方法休眠的线程】

public final void notifyAll()
【唤醒所有休眠的线程】

解决数据重复现象:

class Animal implements Serializable {
	private String name;// 动物的名字
	private String desc;// 动物的描述
	//这是生产标记,如果为true表示可以生产但是不能消费   如果为false表示可以消费但是不能生产
	private  boolean  flag=true;
	/**
	 * 设置名字和描述 该方法是生产者调用
	 * @param name
	 * @param desc
	 */
	public synchronized void set(String name, String desc) {
		if(!this.flag) {//表示需要等待
			try {
				super.wait();  //生产者线程等待
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		this.name = name;
		try {
			Thread.sleep(100);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		this.desc = desc;
		//生产完毕之后要修改生产标记
		this.flag=false;
		//唤醒消费者线程
		super.notifyAll();
	}
	/**
	 * 消费者线程调用的方法
	 */
	public synchronized void get() {
	    if(this.flag) { //如果为true 则表示只能生产,消费者要休眠
		    try {
				super.wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
	    }
		try {
			Thread.sleep(100);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println(this.name + "-->" + this.desc);
		//消费完毕之后要修改生产标记 告诉生产者可以生产了
		this.flag=true;
		//唤醒生产者对象
		super.notifyAll();
	}
}

在这里插入图片描述

此时使用线程之间的信息通讯解决了数据的重复现象。

面试题:

请说明wait()和sleep()的区别:

   wait
      |-是Object类的方法
      |-休眠之后需要使用notify()或者notifyAll()唤醒
      |-休眠之后会释放锁
   sleep
      |-是Thread类的方法
      |-休眠之后会自动醒来
      |-休眠之后不会释放锁
  wait()和notify()是实现线程之间通信的基础方式

猜你喜欢

转载自blog.csdn.net/qq_42539533/article/details/88075835