Java编程--多线程(生产者和消费者问题)

知识点:

1. 生产者和消费者问题的产生;

2. Object类对多线程的支持。

 

一.问题的引出

生产者和消费者指的是两个不同的线程类对象,操作同一资源的情况。具体操作流程如下:

(1)生产者负责生产数据,消费者负责取走数据;

(2)生产者每生产完一组数据之后,消费者就要取走一组数据。

假设要生产的数据如下:

(1)第一组数据:name = Jack ,content = 大学生

(2)第二组数据:name = Mike ,content = 中学生

【范例】程序基本模型。

存储数据的类Info:

class Info {
	String name;
	String content;

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getContent() {
		return content;
	}

	public void setContent(String content) {
		this.content = content;
	}
}

生产者类Producer:

class Producer implements Runnable {
	private Info info;

	public Producer(Info info) {
		this.info = info;
	}

	@Override
	public void run() {
		for (int i = 0; i < 100; i++) {
			if (i % 2 == 0) {
				this.info.setName("Jack");
				try {
					Thread.sleep(100);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				this.info.setContent("大学生");
			} else {
				this.info.setName("Mike");
				this.info.setContent("中学生");
			}
		}
	}
}

消费者类Customer:

class Customer implements Runnable {
	private Info info;

	public Customer(Info info) {
		this.info = info;
	}

	@Override
	public void run() {
		for (int i = 0; i < 100; i++) {
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println(this.info.getName() + "---" + this.info.getContent());
		}
	}
}

主函数:

public class Test {
	public static void main(String[] args) throws Exception {
		Info info = new Info();
		new Thread(new Producer(info)).start();
		new Thread(new Customer(info)).start();
	}
}

运行结果:

python多线程实现生产者消费者问题 java多线程生产者消费者_python多线程实现生产者消费者问题

通过以上代码发现两个严重问题:

(1)数据错位:不再是所需要的完整数据;

(2)数据重复取出,数据重复设置。


二.同步处理

数据的错位完全由非同步的操作所造成的,所以应该使用同步处理。因为取和设置是两个不同的操作,要想进行同步设置,就需要将其定义在一个类里面。

【案例】给程序加上同步。

class Info {
	String name;
	String content;

	public synchronized void set(String name, String content) {
		this.name = name;
		try {
			Thread.sleep(100);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		this.content = content;
	}

	public synchronized void get() {
		try {
			Thread.sleep(100);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println(this.name + "---" + this.content);
	}

}

class Producer implements Runnable {
	private Info info;

	public Producer(Info info) {
		this.info = info;
	}

	@Override
	public void run() {
		for (int i = 0; i < 100; i++) {
			if (i % 2 == 0) {
				this.info.set("Jack","大学生");
			} else {
				this.info.set("Mike","中学生");
			}
		}
	}
}

class Customer implements Runnable {
	private Info info;

	public Customer(Info info) {
		this.info = info;
	}

	@Override
	public void run() {
		for (int i = 0; i < 100; i++) {
			this.info.get();
		}
	}
}

public class Test {
	public static void main(String[] args) throws Exception {
		Info info = new Info();
		new Thread(new Producer(info)).start();
		new Thread(new Customer(info)).start();
	}
}

运行结果:

python多线程实现生产者消费者问题 java多线程生产者消费者_数据_02

此时,数据的错位问题已经得到了很好的解决,但是重复操作问题更加严重了。

 

三.Object类支持

若要想实现整个代码的操作,必须加入等待与唤醒机制。在Object里面提供有专门的处理方法。

(1)等待:

public final void wait() throws InterruptedException

在其他线程调用此对象的 notify() 方法或 notifyAll() 方法前,导致当前线程等待。

(2)唤醒第一个等待线程:

public final void notify()

唤醒在此对象监视器上等待的单个线程。如果所有线程都在此对象上等待,则会选择唤醒其中一个线程。

(3)唤醒全部等待线程:(哪个优先级高就先执行)

public final void notifyAll()

唤醒在此对象监视器上等待的所有线程。

【范例】解决程序问题。



class Info {
	String name;
	String content;
	private boolean flag = true;

	// flag为true表示可以生产,但是不能取走;false表示可以取走,但是不能生产
	public synchronized void set(String name, String content) {
		// 重复进入到set()方法里,发现不能生产,故需要等待
		if (this.flag == false) {
			try {
				super.wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		this.name = name;
		try {
			Thread.sleep(100);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		this.content = content;
		this.flag = false; // 生产完成后修改生产标记
		super.notify(); // 唤醒其他等待线程
	}

	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.content);
		this.flag = true;
		super.notify();
	}

}

class Producer implements Runnable {
	private Info info;

	public Producer(Info info) {
		this.info = info;
	}

	@Override
	public void run() {
		for (int i = 0; i < 100; i++) {
			if (i % 2 == 0) {
				this.info.set("Jack", "大学生");
			} else {
				this.info.set("Mike", "中学生");
			}
		}
	}
}

class Customer implements Runnable {
	private Info info;

	public Customer(Info info) {
		this.info = info;
	}

	@Override
	public void run() {
		for (int i = 0; i < 100; i++) {
			this.info.get();
		}
	}
}

public class Test {
	public static void main(String[] args) throws Exception {
		Info info = new Info();
		new Thread(new Producer(info)).start();
		new Thread(new Customer(info)).start();
	}
}

运行结果:

python多线程实现生产者消费者问题 java多线程生产者消费者_ide_03

面试题:请解释sleep()与wait()的区别?

(1)sleep()是Thread类定义的方法,wait()是Object类定义的方法。

(2)sleep()可以设置休眠时间,时间一到自动唤醒,而wait()需要等待notify()进行唤醒。