1、“生产者与消费者”编程的基础模型【
该程序存在两个问题:
- 数据错位:假设生产者线程刚向数据存储空间添加了信息的名称,还没有加入这个信息的内容,程序就切换到了消费者线程,而消费者线程将把这个信息的名称和上一个信息的内容联系到类一起。
- 重复操作:生产者放了若干次的数据,消费者才开始取出数据;或者是消费者取完一个数据后,还没等到生产者放入新的数据,又重复取出已取过的数据。
】:
package com.mydemo;
public class ThreadDemo {
public static void main(String[] args) {
// 定义Message对象,用于保存和取出数据
Message message = new Message();
// 启动生产者线程
new Thread(new Producer(message)).start();
// 取得消费者线程
new Thread(new Consumer(message)).start();
}
}
class Message {
private String title; // 保存信息的标题
private String content; // 保存信息的内容
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
}
// 定义生产者
class Producer implements Runnable {
private Message msg = null;
/**
* 单参构造
*
* @param msg
*/
public Producer(Message msg) {
this.msg = msg;
}
@Override
public void run() {
// 生产50次数据
for (int i = 0; i < 50; i++) {
if (i % 2 == 0) {
// 设置title属性
this.msg.setTitle("张三");
try {
// 延迟操作
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.msg.setContent("java讲师");
} else {
this.msg.setTitle("李四");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.msg.setContent("JS讲师");
}
}
}
}
// 定义消费者
class Consumer implements Runnable {
private Message msg = null;
/**
* @param msg
*/
public Consumer(Message msg) {
this.msg = msg;
}
@Override
public void run() {
// 取走50次
for (int i = 0; i < 50; i++) {
try {
// 延迟
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(this.msg.getTitle() + "-->" + this.msg.getContent());
}
}
}
2-1、解决上面代码数据同步的问题:
package com.mydemo;
public class ThreadDemo {
public static void main(String[] args) {
// 定义Message对象,用于保存和取出数据
Message message = new Message();
// 启动生产者线程
new Thread(new Producer(message)).start();
// 取得消费者线程
new Thread(new Consumer(message)).start();
}
}
class Message {
private String title; // 保存信息的标题
private String content; // 保存信息的内容
/**
* 在本程序中,生产者和消费者代表着不同的线程对象,
* 所以此时的同步操作应该设置在Message类中,
* 可以将title和content属性设置定义为单独同步方法。
*
* 这样使得不同线程在进行公共数据区域操作时都可以保证数据的完整性,
* 解决了数据设置错乱的问题。
*/
/**
* @param title
* @param content
*/
public synchronized void set(String title, String content) {
this.title = title;
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.content = content;
}
/**
* @return
*/
public synchronized String get() {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
return this.title + "-->" + this.content;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
}
// 定义生产者
class Producer implements Runnable {
private Message msg = null;
/**
* 单参构造
*
* @param msg
*/
public Producer(Message msg) {
this.msg = msg;
}
@Override
public void run() {
// 生产50次数据
for (int i = 0; i < 50; i++) {
if (i % 2 == 0) {
// 设置title属性
this.msg.set("张三", "java讲师");
try {
// 延迟操作
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
this.msg.set("李四", "js讲师");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
// 定义消费者
class Consumer implements Runnable {
private Message msg = null;
/**
* @param msg
*/
public Consumer(Message msg) {
this.msg = msg;
}
@Override
public void run() {
// 取走50次
for (int i = 0; i < 50; i++) {
try {
// 延迟
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(this.msg.get());
}
}
}
2-2、解决上面代码数据同步的问题【用Object线程等待与唤醒方式】:
package com.mydemo;
public class ThreadDemo {
public static void main(String[] args) {
// 定义Message对象,用于保存和取出数据
Message message = new Message();
// 启动生产者线程
new Thread(new Producer(message)).start();
// 取得消费者线程
new Thread(new Consumer(message)).start();
}
}
class Message {
private String title; // 保存信息的标题
private String content; // 保存信息的内容
/**
* 表示生成或消费的形式
* flag = true: 允许生产,但是不允许消费
* flag = false:允许消费,但是不允许生成
*/
private boolean flag = true;
/**
* @param title
* @param content
*/
public synchronized void set(String title, String content) {
// 无法进行生成,等待被消费
if (this.flag == false) {
try {
super.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.title = title;
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.content = content;
// 已经生产过了
this.flag = false;
// 唤醒等待的线程
super.notify();
}
/**
* @return
*/
public synchronized String get() {
// 还未生产,需要等待
if (this.flag == true) {
try {
super.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
return this.title + "-->" + this.content;
} finally {
// 不管如何都要执行
this.flag = true; // 继续生产
super.notify(); // 唤醒等待线程
}
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
}
// 定义生产者
class Producer implements Runnable {
private Message msg = null;
/**
* 单参构造
*
* @param msg
*/
public Producer(Message msg) {
this.msg = msg;
}
@Override
public void run() {
// 生产50次数据
for (int i = 0; i < 50; i++) {
if (i % 2 == 0) {
// 设置title属性
this.msg.set("张三", "java讲师");
try {
// 延迟操作
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
this.msg.set("李四", "js讲师");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
// 定义消费者
class Consumer implements Runnable {
private Message msg = null;
/**
* @param msg
*/
public Consumer(Message msg) {
this.msg = msg;
}
@Override
public void run() {
// 取走50次
for (int i = 0; i < 50; i++) {
try {
// 延迟
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(this.msg.get());
}
}
}