Java基础之多线程(综合案例)4月打卡day11

举报
java厂长 发表于 2022/04/11 21:12:28 2022/04/11
【摘要】 Java基础之多线程(综合案例)4月打卡day11关于作者作者介绍🍓 博客主页:作者主页🍓 简介:JAVA领域优质创作者🥇、一名在校大三学生🎓、在校期间参加各种省赛、国赛,斩获一系列荣誉🏆。🍓 关注我:关注我学习资料、文档下载统统都有,每日定时更新文章,励志做一名JAVA资深程序猿👨‍💻。1、多线程要使用多线程必须有一个前提,有一个线程的执行主类。从多线程开始,Java正式进...

Java基础之多线程(综合案例)4月打卡day11

关于作者

  • 作者介绍


🍓 博客主页:作者主页

🍓 简介:JAVA领域优质创作者🥇、一名在校大三学生🎓、在校期间参加各种省赛、国赛,斩获一系列荣誉🏆。

🍓 关注我:关注我学习资料、文档下载统统都有,每日定时更新文章,励志做一名JAVA资深程序猿👨‍💻。

1、多线程

要使用多线程必须有一个前提,有一个线程的执行主类。从多线程开始,Java正式进入到应用部分,而对于多线程的开发,从JavaEE上表现的并不是特别多,但是在Android开发之中使用较多,并且需要提醒的是,鄙视面试的过程之中,多线程所问道的问题是最多的。

综合案例

生产者和消费者是一道最为经典的供求案例:provider、consumer。不管是之后的分布式开发还是其他开发都被大量采用。

生产者只是负责生产数据,而生产者每生产一个完整的数据,消费者就把这个数据拿走。假设生产如下数据 title = 生产,note = 出库;title=拿货, note = 消费

image-20210816190031191

package com.day12.demo;
​
class Message{
    private String title;
    private String note;
    public String getTitle() {
        return title;
    }
    public void setTitle(String title) {
        this.title = title;
    }
    public String getNote() {
        return note;
    }
    public void setNote(String note) {
        this.note = note;
    }
    
}
class Productor implements Runnable{
    Message msg = null;
    public Productor(Message msg){
        this.msg=msg;
    }
​
    @Override
    public void run() {
        // TODO 自动生成的方法存根
        for(int x = 0;x<50;x++){
            if(x%2==0){
                try {
                    msg.setTitle("生产");
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    // TODO 自动生成的 catch 块
                    e.printStackTrace();
                }
                msg.setNote("出库");
            }else{
                msg.setNote("拿货");
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    // TODO 自动生成的 catch 块
                    e.printStackTrace();
                }
                msg.setNote("消费");
            }
        }
    }
}
class Consumer implements Runnable{
    Message msg = null;
    public Consumer(Message msg){
        this.msg=msg;
    }
    @Override
    public void run() {
        // TODO 自动生成的方法存根
        for(int x= 0;x<50;x++){
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                // TODO 自动生成的 catch 块
                e.printStackTrace();
            }
            System.out.println(this.msg.getTitle()+"--->"+this.msg.getNote());
        }
    }
}   
public class PCDemo {
    public static void main(String args[]){
        // TODO 自动生成的方法存根
        Message msg = new Message();
        Productor pro = new Productor(msg);
        Consumer con = new Consumer(msg);
        new Thread(pro).start();
        new Thread(con).start();
    }
}


但是异常的代码模型出现了如下的两个严重的问题:

  1. 数据错位了

  2. 出现了重复取出和重复设置的问题

1.解决数据错位问题:依靠同步解决
package com.day12.demo;
​
class Message {
    private String title;
    private String note;
​
    public synchronized void set(String title, String note) {
        try {
            Thread.sleep(50);
        } catch (InterruptedException e) {
            // TODO 自动生成的 catch 块
            e.printStackTrace();
        }
        this.title = title;
​
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            // TODO 自动生成的 catch 块
            e.printStackTrace();
        }
        this.note = note;
    }
​
    public synchronized void get() {
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            // TODO 自动生成的 catch 块
            e.printStackTrace();
        }
        System.out.println(this.title + "--->" + this.note);
    }
}
​
class Productor implements Runnable {
    Message msg;
​
    public Productor(Message msg) {
        this.msg = msg;
    }
​
    @Override
    public void run() {
        // TODO 自动生成的方法存根
        for (int x = 0; x < 50; x++) {
            if (x % 2 == 0) {
                msg.set("生产", "出库");
            } else {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    // TODO 自动生成的 catch 块
                    e.printStackTrace();
                }
                msg.set("拿货", "消费");
            }
        }
    }
}
​
class Consumer implements Runnable {
    Message msg = null;
​
    public Consumer(Message msg) {
        this.msg = msg;
    }
​
    @Override
    public void run() {
        // TODO 自动生成的方法存根
        for (int x = 0; x < 50; x++) {
            msg.get();
        }
    }
}
​
public class PCDemo {
    public static void main(String args[]) {
        // TODO 自动生成的方法存根
        Message msg = new Message();
        Productor pro = new Productor(msg);
        Consumer con = new Consumer(msg);
        new Thread(pro).start();
        new Thread(con).start();
    }
}

虽然解决了错位的问题,但是重复设置重复取出更加严重了。

2.解决数据的重复设置和重复取出

要想解决重复的问题需要等待及唤醒机制,而这一机制的实现只能依靠Object类完成,在Object类之中定义了以下的三个方法完成线程操作:

方法名称 类型 描述
public final void wait() throws InterruptedException 普通 等待、死等
public final void notify() 普通 唤醒第一个等待线程
public final void notifyAll() 普通 唤醒全部等待线程

通过等待与唤醒机制来解决数据的重复操作问题

package com.day12.demo;
​
class Message {
    private String title;
    private String note;
    //flag = true  允许生产但是不允许消费者取走
    //flag = false 生产完毕 允许消费者取走,但是不能够生产
    private boolean flag = false;
    public synchronized void set(String title, String note) {
        if(flag == true){//生产完毕 允许消费者取走,但是不能够生产
            try {
                super.wait();
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        this.title = title;
        try {
            Thread.sleep(10);
        } catch (InterruptedException e) {
            // TODO 自动生成的 catch 块
            e.printStackTrace();
        }
        this.note = note;
        flag = true;
        super.notify();
    }
​
    public synchronized void get() {
        if(flag == false){ //允许生产但是不允许消费者取走
            try {
                super.wait();
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        try {
            Thread.sleep(50);
        } catch (InterruptedException e) {
            // TODO 自动生成的 catch 块
            e.printStackTrace();
        }
        System.out.println(this.title + "--->" + this.note);
        flag = false;
        super.notify();
    }
}
​
class Productor implements Runnable {
    Message msg;
​
    public Productor(Message msg) {
        this.msg = msg;
    }
​
    @Override
    public void run() {
        // TODO 自动生成的方法存根
        for (int x = 0; x < 50; x++) {
            if (x % 2 == 0) {
                msg.set("生产", "出库");
            } else {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    // TODO 自动生成的 catch 块
                    e.printStackTrace();
                }
                msg.set("拿货", "消费");
            }
        }
    }
}
​
class Consumer implements Runnable {
    Message msg = null;
​
    public Consumer(Message msg) {
        this.msg = msg;
    }
​
    @Override
    public void run() {
        // TODO 自动生成的方法存根
        for (int x = 0; x < 50; x++) {
            msg.get();
        }
    }
}
​
public class PCDemo {
    public static void main(String args[]) {
        // TODO 自动生成的方法存根
        Message msg = new Message();
        Productor pro = new Productor(msg);
        Consumer con = new Consumer(msg);
        new Thread(pro).start();
        new Thread(con).start();
    }
}

面试题:请解释sleep()和wait()的区别?

  • sleep()是Thread类中定义的方法,到了一定的时间后该休眠的线程自动唤醒,自动唤醒。

  • wait()是Object类中定义的方法,如果要想唤醒,必须使用notify()、notifyAll()才能唤醒,手动唤醒。



# 后语

厂长写博客目的初衷很简单,希望大家在学习的过程中少走弯路,多学一些东西,对自己有帮助的留下你的赞赞👍或者关注➕都是对我最大的支持,你的关注和点赞给厂长每天更文的动力。

对文章其中一部分不理解,都可以评论区回复我,我们来一起讨论,共同学习,一起进步!

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。