生产者——消费者
生产者消费者模式 :不属于设计模式 只是线程之间通信的一种场景
生产什么消费什么
没有生产 不能消费
不能重复消费同一个产品
保证产品的完整性
例子——电脑购买练习代码
电脑类:
public class Computer {
private String mainFrame;
private String screen;
// false 可以生产 不能消费
// true 可以消费 不能生产
private boolean flag; // 是否可以生产/消费的牌子
public boolean isFlag() {
return flag;
}
public void setFlag(boolean flag) {
this.flag = flag;
}
public String getMainFrame() {
return mainFrame;
}
public void setMainFrame(String mainFrame) {
this.mainFrame = mainFrame;
}
public String getScreen() {
return screen;
}
public void setScreen(String screen) {
this.screen = screen;
}
public Computer() {
}
public Computer(String mainFrame, String screen) {
this.mainFrame = mainFrame;
this.screen = screen;
}
@Override
public String toString() {
return "Computer{" +
"mainFrame='" + mainFrame + '\'' +
", screen='" + screen + '\'' +
'}';
}
}
生产者:
public class Producer extends Thread{
private Computer computer;
public Producer(Computer computer) {
this.computer = computer;
}
@Override
public void run() {
for(int i = 1;i <= 20;i++){
synchronized (computer) {
if(computer.isFlag() == true){
try {
computer.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if(i % 2 != 0){
computer.setMainFrame(i + "号联想主机");
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
computer.setScreen(i + "号联想显示器");
System.out.println("生产了" + i + "号联想电脑");
}else{
computer.setMainFrame(i + "号华硕主机");
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
computer.setScreen(i + "号华硕显示器");
System.out.println("生产了" + i + "号华硕电脑");
}
computer.setFlag(true);
computer.notify();
}
}
}
}
消费者:
public class Consumer extends Thread {
private Computer computer;
public Consumer(Computer computer) {
this.computer = computer;
}
@Override
public void run() {
for (int i = 1; i <= 10; i++) {
synchronized (computer) {
while (true) {
if (computer.getFlag() == 0 || computer.getFlag() == 1) {
try {
computer.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
break;
}
}
System.out.println("消费了第" + i + "号" + computer);
computer.setFlag(0);
computer.notifyAll();
}
}
}
}
测试类:
public class Test {
public static void main(String[] args) {
Computer computer = new Computer();
Producer producer = new Producer(computer);
Consumer consumer = new Consumer(computer);
producer.setName("生产者");
consumer.setName("消费者");
producer.start();
consumer.start();
}
}
以上实现效果为:生产一个就消费一个,自然不太符合现实生活,后续会利用队列来真实地模拟出符合现实生活的操作(~ ̄▽ ̄)~
改写代码
( •̀ ω •́ )✧心潮澎湃下,小编突然觉得两个线程还是有些单调了,众所周知,生厂商和消费者中间可以有一个线上 / 线下店铺,那是不是也可以将其当作一个进程呢?
于是,我在原基础上改写了一下,代码如下:
电脑类:
public class Computer {
private String mainFrame;
private String screen;
//0,表示可以生产,不能进货,不能消费
//1,表示不能生产,可以进货,但处于进货中,不能消费
//2,表示不能生产,不能进货,但已经进完货,可以消费
private int Flag;
public Computer() {
}
public Computer(String mainFrame, String screen) {
this.mainFrame = mainFrame;
this.screen = screen;
}
public String getMainFrame() {
return mainFrame;
}
public void setMainFrame(String mainFrame) {
this.mainFrame = mainFrame;
}
public String getScreen() {
return screen;
}
public void setScreen(String screen) {
this.screen = screen;
}
public int getFlag() {
return Flag;
}
public void setFlag(int flag) {
Flag = flag;
}
@Override
public String toString() {
return "Computer{" +
"mainFrame='" + mainFrame + '\'' +
", screen='" + screen + '\'' +
", Flag=" + Flag +
'}';
}
}
生产者:
public class Producer extends Thread {
private Computer computer;
public Producer(Computer computer) {
this.computer = computer;
}
@Override
public void run() {
for (int i = 1; i <= 10; i++) {
synchronized (computer) {
while (true) {
if (computer.getFlag() == 1 || computer.getFlag() == 2) {
try {
computer.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
} else {
break;
}
}
if (i % 2 != 0) {
computer.setMainFrame(i + "号联想主机");
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
computer.setScreen(i + "号联想显示器");
System.out.println("生产了" + i + "号联想电脑");
} else {
computer.setMainFrame(i + "号华硕主机");
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
computer.setScreen(i + "号华硕显示器");
System.out.println("生产了" + i + "号华硕电脑");
}
computer.setFlag(1);
//这里为何是computer.notify()?
computer.notifyAll();
}
}
}
}
消费者:
public class Consumer extends Thread {
private Computer computer;
public Consumer(Computer computer) {
this.computer = computer;
}
@Override
public void run() {
for (int i = 1; i <= 10; i++) {
synchronized (computer) {
while (true) {
if (computer.getFlag() == 0 || computer.getFlag() == 1) {
try {
computer.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
break;
}
}
System.out.println("消费了第" + i + "号" + computer);
computer.setFlag(0);
computer.notifyAll();
}
}
}
}
商店:
public class Store extends Thread {
private Computer computer;
public Store(Computer computer) {
this.computer = computer;
}
@Override
public void run() {
for (int i = 1; i <= 10; i++) {
synchronized (computer) {
while (true) {
if (computer.getFlag() == 0 || computer.getFlag() == 2) {
try {
computer.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
break;
}
}
System.out.println("进货了第" + i + "号" + computer);
computer.setFlag(2);
computer.notifyAll();
}
}
}
}
测试类:
public class Test {
public static void main(String[] args) {
Computer computer = new Computer();
Producer producer = new Producer(computer);
Consumer consumer = new Consumer(computer);
Store store = new Store(computer);
producer.setName("生产者");
store.setName("电脑商店");
consumer.setName("消费者");
producer.start();
store.start();
consumer.start();
}
}
问题解答
(●ˇ∀ˇ●)心细的读者可能发现了,Flag判断又原本的一层变成了多层,这与线程运行和父类提供的方法有关。
起初,当生产者线程执行时,商店线程和消费者线程则进入休眠状态,即wait(),当生产者唤醒所有程序时,商店线程和消费者线程都会被唤醒。
然而生产者线程执行完后,下一个线程并不一定是商店线程,也可能是消费者线程。
除外,读者可能也有些疑惑了,为啥代码中调用的是唤醒所有线程 notifyAll() 方法,而不是只会唤醒一个线程的 notify() 方法,亦或者直接指向商店线程呢?
众所周知,线程的执行先后顺序,不是由线程决定的,是由CPU决定的。
ψ(`∇´)ψ正如爱情公寓张伟所说:“拿什么和你赌不是看你要什么,而是看我有什么!”
于是,无论是被唤醒的商店线程或消费者线程,都会从 wait() 后面代码执行,并不会再次进入判断 if。
那怎么办呢?增加多一层相同判断 if ?
开始时我也是这样想的,并为每个线程增加了一层相同判断 if,控制台输出的结果也达到了小编的期待,但在小编手贱重复运行整个程序时,Σ(っ °Д °;)っ竟意外发现有小概率打印的结果会乱序。
然而,赶去厕所的路上,我突然想到,如果两个线程要一层判断 if,三个线程要两层判断** if ,那 n 个线程岂不是要 n-1 个判断 if **?
于是在o( ̄▽ ̄)ブ厕所之神的帮助下,我灵光一闪,想到了利用循环判断来解决该问题。
正如网络名言“ 如果一发导弹不能杀死敌人,那就两发,如果两发还不够就三发!”ヾ(≧▽≦*)o
最后,如果大家还要增加多个线程的话,可以将判断条件改简洁点,毕竟优秀的程序员是不会让代码繁乱的○( ^皿^)っHiahiahia…
备注:大神看了觉得还能再简洁或者有更强的操作可以在私聊我,亦或评论区评论下!