Java多线程:生产者与消费者案例

网友投稿 273 2022-12-24

Java多线程:生产者与消费者案例

目录前言工具知识点设计思路具体步骤总结

前言

想象一下生活中哪些是和线程沾边的?饭店炒菜就是一个很好的例子

首先客人要吃菜,前提是厨师要炒好,也就是说,厨师不炒好的话客人是没有饭菜的。这时候,厨师就是一个线程,客人拿菜就是另一个线程。

工具

jdk13,IDEA2019.1.4

知识点

Thread、Runnable、synchronized、面向对象知识(继承、封装、接口、方法重写)、条件判断以及线程的一些其他知识点

设计思路

首先要有两个线程,也就是说要两个类,分别是Producer(生产者)和Consumer(消费者)。

由于我们是模拟厨师与客人之间的互动,也就是说还需要一个类来封装信息:Message(类)。

然后,避免线程之间发生数据混乱的情况,肯定还需要使用synchronized来进行线程同步。

具体步骤

首先我们来一份没有用synchronized的代码,先看看效果:

public class Message {

private String title;

private String content;

Message(){

};

public Message(String title, String content) {

this.title = title;

this.content = 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;

}

}

/*

* 定义生产者类Producer

* */

class Producer implements Runnable{

private Message msg=null;

public Producer(Message msg) {

this.msg = msg;

}

@Override

public void run() {

for (int i=0;i<=50;i++){

if (i%2==0){

this.msg.setTitle("喜欢夜雨吗?");

try {

Thread.sleep(100);

}catch (InterruptedException e){

System.out.println(e);

}

this.msg.setContent("是的呢!");

}else {

this.msg.setTitle("还不关注我吗?");

try {

Thread.sleep(100);

}catch (InterruptedException e){

System.out.println(e);

}

this.msg.setContent("好的呢!");

}

}

}

}

/*

* 定义消费者类Consumer

* */

class Consumer implements Runnable{

private Message msg=null;

public Consumer(Message msg) {

this.msg = msg;

}

@Override

public void run() {

for (int i=0;i<=50;i++){

try {

Thread.sleep(100);

}catch (InterruptedException e){

System.out.println(e);

}

System.out.println(this.msg.getTitle()+"--->"+this.msg.getContent());

}

}

}

class TestDemo{

public static void main(String[] args) {

Message msg=new Message();

new Thread(new Producer(msg)).start();

new Thread(new Consumer(msg)).start();

}

}

看看运行结果:

看仔细咯,发生了数据错乱啊,Title与Content没有一一对应欸。咋办?

能咋办,改代码呗。

发生数据混乱的原因完全是因为,生产者线程还没生产的时候,消费者就已经消费了(至于消费的啥我也不知道,我也不敢问啊)。所以造成了数据混乱,不过我们上面说了啊,要使用synchronized来让线程同步一下。但是又会出问题,我们接着往下看

class TestDemo{

public static void main(String[] args) {

Message msg=new Message();

new Thread(new Producer(msg)).start();

new Thread(new Consumer(msg)).start();

}

}

class Message{

private String title,content;

public synchronized void set(String title,String content){

this.title=title;

this.content=content;

}

public synchronized void get(){

try {

Thread.sleep(1000);

}catch (InterruptedException e){

System.out.println(e);

}

System.out.println(this.title+"-->"+this.content);

}

public String getContent() {

http:// return content;

}

public void setContent(String content) {

this.content = content;

}

public String getTitle() {

return title;

}

public void setTitle(String title) {

this.title = title;

}

}

class Producer implements Runnable{

private Message msg=null;

Producer(Message msg){

this.msg=msg;

}

@Override

public void run() {

for (int i=0;i<=50;i++){

if (i%2==0){

this.msg.set("喜欢夜雨吗?","是的呢!");

}else {

this.msg.set("还不关注吗?","好的呢!");

}

}

}

}

class Consumer implements Runnable{

private Message msg=null;

Consumer(Message msg){

this.msg=msg;

}

@Override

public void run() {

for (int i=0;i<=50;i++){

this.msg.get();

}

}

}

我又重新封装了一些方法,然后运行的时候,wc,数据倒是不混乱了,但是呢,数据重复了一大堆。

为啥会出现这个问题呢?最后想了一下,会出现这种问题的,就是因为线程的执行顺序的问题。我们想要实现的效果是生产者线程执行了之后,让生产者线程等待,而后让消费者线程执行,等待消费者线程执行完成之后就又让生产者线程继续执行。后来我又查了查官方文档,发现Object类中专门有三个方法是与线程相关的:

方法

描述

public final void wait() throws InterruptedException

线程的等待

public final void notify()

唤醒第一个等待线程

public final void notifyAll()

当我看到这些方法的时候,心里愣了一下,这不就是我们想要的方法吗,用wait()方法来让生产者线程等待,然后运行消费者线程,等消费者线程执行完了之后又让生产者线程去执行。这其中我们用true和false来表示运行开始和运行暂停。

最后我们来看看完成品的代码:

class TestDemo{

public static void main(String[] args) {

Message msg=new Message();

new Thread(new Producer(msg)).start();

new Thread(new Consumer(msg)).start();

}

}

class Message extends Exception{

private String title,content;

private boolean flag=true;

// true表示正在生产,不要来取走,因为没由让消费者区走的

// false表示可以取走,但是不能生产

public synchronized void set(String title,String content){

if (this.flag==false){

try {

super.wait();

}catch (InterruptedException e){

System.out.println(e);

}

}

this.title=title;

try {

Thread.sleep(60);

}catch (InterruptedException e){

System.out.println(e);

}

this.content=content;

this.flag=true; // 生产完成,修改标志位

super.notify(); // 唤醒等待线程

}

public synchronized void get(){

if (flag==true) {// 已经生产好了,等待取走

try {

super.wait();

}catch (InterruptedException e){

System.out.println(e);

}

}

try {

Thread.sleep(60);

}catch (InterruptedException e){

System.out.println(e);

}

System.out.println(this.title+"-->"+this.content);

this.flag=true;

super.notify();

}

public String getContent() {

return content;

}

public void setContent(String content) {

this.content = content;

}

public String getTitle() {

return title;

}

public void setTitle(String title) {

this.title = title;

}

}

class Producer implements Runnable{

private Message msg=null;

Producer(Message msg){

this.msg=msg;

}

@Override

public void run() {

for (int i=0;i<=50;i++){

if (i%2==0){

this.msg.set("喜欢夜雨吗?","是的呢!");

}else {

this.msg.set("还不关注吗?","好的呢!");

}

}

}

}

class Consumer implements http://Runnable{

private Message msg=null;

Consumer(Message msg){

this.msg=msg;

}

@Override

public void run() {

for (int i=0;i<=50;i++){

this.msg.get();

}

}

}

运行结果我就不贴了,你们自己去测试一下吧…

总结

这个案例完美的呈现了线程以及面向对象知识的综合运用,具有很强的实际操作性

本篇文章就到这里了,希望能给你带来帮助,也希望您能够多多关注我们的更多内容!

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:springBoot2.X配置全局捕获异常的操作
下一篇:Springboot使用redis实现接口Api限流的实例
相关文章

 发表评论

暂时没有评论,来抢沙发吧~