03-生产者消费者实战
- 生产者和消费者问题的产生
- Object 类多多线程的支持
问题的引出
生产者和消费者是两个不同的线程类对象,操作同一资源的情况。
- 生产者生产数据,消费者幅取走数据;
- 生产者每生产完一组数据之后,消费者就要取走一组数据
class Info {
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 Productor implements Runnable {
private Info info;
public Productor(Info info) {
this.info = info;
}
@Override
public void run() {
for (int x = 0; x < 100; x++) {
if (x % 2 == 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.info.setTitle("AAA");
this.info.setContent("AAA---");
} else {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.info.setTitle("BBB");
this.info.setContent("BBB---");
}
}
}
}
class Customer implements Runnable {
private Info info;
public Customer(Info info) {
this.info = info;
}
@Override
public void run() {
for (int x = 0; x < 100; x++) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(this.info.getTitle() + "-" + this.info.getContent());
}
}
}
public class Main {
public static void main(String[] args) {
Info info = new Info();
new Thread(new Productor(info)).start();
new Thread(new Customer(info)).start();
}
}
以上代码会产生脏读,重复读的问题。
- 数据错位
- 数据重复取出,重复设置
解决数据错乱问题
因为非同步的操作所造成的,所以应该使用同步处理(synchronized)
因为设置和取想进行同步控制,所以要定义在一个类里面完成。
class Info {
private String title;
private String content;
public synchronized void set(String title,String content){
this.title=title;
try {
Thread.sleep(200);
} 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.title + "-" + this.content);
}
}
class Productor implements Runnable {
private Info info;
public Productor(Info info) {
this.info = info;
}
@Override
public void run() {
for (int x = 0; x < 100; x++) {
if (x % 2 == 0) {
this.info.set("AAA","AAA---");
} else {
this.info.set("BBB","BBB---");
}
}
}
}
class Customer implements Runnable {
private Info info;
public Customer(Info info) {
this.info = info;
}
@Override
public void run() {
for (int x = 0; x < 100; x++) {
this.info.get();
}
}
}
public class Main {
public static void main(String[] args) {
Info info = new Info();
new Thread(new Productor(info)).start();
new Thread(new Customer(info)).start();
}
}
上述代码解决了数据错乱的问题,但是重复操作的问题更加严重。
解决重复的问题
如果要实现整个代码的操作,但是必须加入等待与唤醒机制。
在 Object 类中提供了专有方法。
- 等待方法:
public final void wait() throws InterruptedException { }
- 唤醒第一个等待线程:
public final native void notify();
- 唤醒全部等待线程,哪个优先级高就先执行哪一个:
public final native void notifyAll();
因此使用上述方法解决重复问题:
class Info {
private String title;
private String content;
// True 表示可以生产,不可以取走
// False 表示可以取走,不可以生产
private boolean flag = true;
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(200);
} 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.title + "-" + this.content);
this.flag=true;
super.notify();
}
}
class Productor implements Runnable {
private Info info;
public Productor(Info info) {
this.info = info;
}
@Override
public void run() {
for (int x = 0; x < 100; x++) {
if (x % 2 == 0) {
this.info.set("AAA","AAA---");
} else {
this.info.set("BBB","BBB---");
}
}
}
}
class Customer implements Runnable {
private Info info;
public Customer(Info info) {
this.info = info;
}
@Override
public void run() {
for (int x = 0; x < 100; x++) {
this.info.get();
}
}
}
public class Main {
public static void main(String[] args) {
Info info = new Info();
new Thread(new Productor(info)).start();
new Thread(new Customer(info)).start();
}
}
至此,程序按照我们的预期执行。生产一个取一个。
面试题:解释 sleep 和 wait 的区别?
- sleep()是 Thread 类定义的方法,wait()是 Object 类定义的方法
- sleep()可以设置休眠时间,时间到了自动唤醒,wait()必须等待 notify()唤醒。