一、问题引出
本程序的核心结构如下:首先定义两个类,一个是生产者线程,另外一个消费者线程类,生产者每生产完一个数据之后,消费者要取走这些数据,那么假设现在的数据有两种:
- title = 小动物,content = 草泥马;
- title = 小金子,content = 不是好孩子。
范例:代码基本模型
class Message { private String title; private String content; public void setTitle(String title) { this.title = title; } public void setContent(String content) { this.content = content; } public String getContent() { return content; } public String getTitle() { return title; } } class Productor implements Runnable { private Message msg; public Productor(Message msg) { this.msg = msg; } @Override public void run() { for (int x = 0; x < 100; x++) { if (x % 2 == 0) { this.msg.setTitle("小动物"); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } this.msg.setContent("草泥马"); } else { this.msg.setTitle("小金子"); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } this.msg.setContent("不是好孩子"); } } } } class Customer implements Runnable { private Message msg; public Customer(Message msg) { this.msg = msg; } @Override public void run() { for (int x = 0; x < 100; x++) { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(this.msg.getTitle() + "," + this.msg.getContent()); } } } public class TestDemo { public static void main(String[] args) { Message msg = new Message(); new Thread(new Productor(msg)).start(); ; new Thread(new Customer(msg)).start(); ; } }
遗憾的是,这个时候出现了两个问题:
- 数据错位;
- 数据重复取出与重复设置。
二、同步处理
要想解决数据的错位,使用同步处理即可,所以代码修改如下。
class Message {
private String title;
private String content;
public synchronized void set(String title, String content) {
this.title = title;
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.content = content;
}
public synchronized void get() {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(this.title + "," + this.content);
}
}
class Productor implements Runnable {
private Message msg;
public Productor(Message msg) {
this.msg = msg;
}
@Override
public void run() {
for (int x = 0; x < 100; x++) {
if (x % 2 == 0) {
this.msg.set("小动物", "草泥马");
} else {
this.msg.set("小金子", "不是好孩子");
}
}
}
}
class Customer implements Runnable {
private Message msg;
public Customer(Message msg) {
this.msg = msg;
}
@Override
public void run() {
for (int x = 0; x < 100; x++) {
this.msg.get();
}
}
}
public class TestDemo {
public static void main(String[] args) {
Message msg = new Message();
new Thread(new Productor(msg)).start();
;
new Thread(new Customer(msg)).start();
;
}
}
现在的数据没有任何错乱,但是重复的操作更严重了。
三、Object类支持
如果要想解决重复的操作必须加入等待与唤醒机制。而这样的处理机制是由Object类提供的方法支持,在Object类之中有如下的方法可以使用:
- 等待:public final void wait() throws InterruptedException;
- 唤醒第一个:public final void notify();
- 唤醒全部:public final void notifyAll(),谁的优先级高就先执行。
范例:解决问题
class Message { private String title; private String content; private boolean flag = true; // flag = true:表示可以生产,但是不能取走 // flag = false:表示可以取走,但是不能生产 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(); } public synchronized void get() { if (this.flag == true) { try { super.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(this.title + "," + this.content); this.flag = true; super.notify(); } }
此部分的代码没有一个绝对的掌握标准,会了总是好的,如果觉得之前的概念已经没问题了,可以好好看看。
扩展题目:请解释sleep()和wait()的区别?
- sleep()是Thread类定义的方法,在休眠之后可以自动唤醒;
- wait()是Object类定义的方法,等待之后必须使用notify()或notifyAll()手工唤醒。