Java基础之多线程(综合案例)4月打卡day11
Java基础之多线程(综合案例)4月打卡day11
关于作者
-
作者介绍
🍓 博客主页:
🍓 简介:JAVA领域优质创作者🥇、一名在校大三学生🎓、在校期间参加各种省赛、国赛,斩获一系列荣誉🏆。
🍓 关注我:关注我学习资料、文档下载统统都有,每日定时更新文章,励志做一名JAVA资深程序猿👨💻。
1、多线程
要使用多线程必须有一个前提,有一个线程的执行主类。从多线程开始,Java正式进入到应用部分,而对于多线程的开发,从JavaEE上表现的并不是特别多,但是在Android开发之中使用较多,并且需要提醒的是,鄙视面试的过程之中,多线程所问道的问题是最多的。
生产者和消费者是一道最为经典的供求案例:provider、consumer。不管是之后的分布式开发还是其他开发都被大量采用。
生产者只是负责生产数据,而生产者每生产一个完整的数据,消费者就把这个数据拿走。假设生产如下数据 title = 生产,note = 出库;title=拿货, note = 消费
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.解决数据错位问题:依靠同步解决
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()才能唤醒,手动唤醒。
# 后语
厂长写博客目的初衷很简单,希望大家在学习的过程中少走弯路,多学一些东西,对自己有帮助的留下你的赞赞👍或者关注➕都是对我最大的支持,你的关注和点赞给厂长每天更文的动力。
对文章其中一部分不理解,都可以评论区回复我,我们来一起讨论,共同学习,一起进步!
- 点赞
- 收藏
- 关注作者
评论(0)