菜鸡的Java笔记 生产者与消费者
生产者与消费者
代码要求知道做什么用即可
线程间的通讯问题以及 Object 类的支持
基础模型
现在希望实现一种数据的生产和取出的操作形式,即:有两个甚至更多的线程对象,这样的线程分为生产者线程和消费者线程
那么最理想的状态是生产者每生产完一条完整的数据之后,消费者就要取走这个数据,并且进行输出的打印
现在假设要输出的信息有这样两个:
title = 帅帅, content = 一个学生;
title = 可爱的小动物, content = 小猫咪;
范例:程序的初期实现
package cn.mysterious.actualcombat;
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 Producptor implements Runnable{
private Info info = null;
public Producptor(Info info){
this.info = info;
}
@Override
public void run() {
// TODO Auto-generated method stub
for (int i = 0; i < 50; i++) {
if (i % 2 == 0) { // 偶数
this.info.setTitle("帅帅");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
this.info.setContent("一个学生");
}else {
this.info.setTitle("可爱的动物");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
this.info.setContent("小猫咪");
}
}
} }
class Consumer implements Runnable{
private Info info = null;
public Consumer(Info info){
this.info = info;
}
@Override
public void run() {
// TODO Auto-generated method stub
for (int i = 0; i < 50; i++) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(this.info.getTitle() + "-->" + this.info.getContent());
}
}
}
public class ActualCombat{
public static void main(String[] args){
Info info = new Info();
Producptor p = new Producptor(info);
Consumer c = new Consumer(info);
new Thread(p).start();
new Thread(c).start(); }
}
通过以上的执行可以发现有两个问题:
第一:数据错位了
第二:重复生产,重复取出
解决数据不同步问题
要想解决同步问题一定使用同步代码块或者是同步方法,既然要同步,那么肯定要将设置属性和取得的属性的内容都统一交给 Info 完成
范例:
package cn.mysterious.actualcombat;
class Info{
private String title;
private String content;
public synchronized void set(String content, String title){
this.title = title;
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
this.content = content;
}
public synchronized void get(){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(this.title + "-->" +this.content );
}
}
class Producptor implements Runnable{
private Info info = null;
public Producptor(Info info){
this.info = info;
}
@Override
public void run() {
// TODO Auto-generated method stub
for (int i = 0; i < 50; i++) {
if (i % 2 == 0) { // 偶数
this.info.set("帅帅","一个学生");
}else {
this.info.set("可爱的动物","小猫咪");
}
}
} }
class Consumer implements Runnable{
private Info info = null;
public Consumer(Info info){
this.info = info;
}
@Override
public void run() {
// TODO Auto-generated method stub
for (int i = 0; i < 50; i++) {
this.info.get();
}
}
}
public class ActualCombat{
public static void main(String[] args){
Info info = new Info();
Producptor p = new Producptor(info);
Consumer c = new Consumer(info);
new Thread(p).start();
new Thread(c).start(); }
}
所有的设置和取得数据的操作都交给了同步方法完成
现在同步问题解决了,但是重复问题更严重了
解决重复操作
如果要想解决重复问题,那么必须加入等待与唤醒的处理机制,而这样的操作方法是有 Object 类所提供的
在 Object 类中提供有如下几种方法:
等待: public final void wait() throws InterruptedException{}
唤醒第一个等待线程: public final void notify();
唤醒全部等待线程: public final void notifyAll();
范例:修改Info类
package cn.mysterious.actualcombat;
class Info{
private String title;
private String content;
private boolean flag = true;
// flag = true 表示可以生产,但是不允许取走数据
// flag = false 表示可以取走数据,但是不允许生产数据
public synchronized void set(String title, String content){
if (this.flag = false) { // 表示已经生产过了,还未取走
try {
super.wait(); // 等待
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} // 没有生产,可以生产
this.title = title;
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
this.content = content;
this.flag = false;// 表示生产过了
super.notify();
}
public synchronized void get(){
if (this.flag = true) { // 此时应该生产,不应该取走数据
try {
super.wait(); // 等待
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(this.title + "-->" +this.content );
this.flag = true; // 表示取走了
super.notify();
}
}
class Producptor implements Runnable{
private Info info = null;
public Producptor(Info info){
this.info = info;
}
@Override
public void run() {
// TODO Auto-generated method stub
for (int i = 0; i < 50; i++) {
if (i % 2 == 0) { // 偶数
this.info.set("帅帅","一个学生");
}else {
this.info.set("可爱的动物","小猫咪");
}
}
} }
class Consumer implements Runnable{
private Info info = null;
public Consumer(Info info){
this.info = info;
}
@Override
public void run() {
// TODO Auto-generated method stub
for (int i = 0; i < 50; i++) {
this.info.get();
}
}
}
public class ActualCombat{
public static void main(String[] args){
Info info = new Info();
Producptor p = new Producptor(info);
Consumer c = new Consumer(info);
new Thread(p).start();
new Thread(c).start(); }
}
面试题:请解释 sleep() 与 wait() 的区别?
sleep() 是 Thread 类定义的方法,在休眠一定时间之后自己唤醒
wait() 是 Object 类定义的方法,表示线程要等待执行,必须通过 notify(),notifyAll() 方法来进行唤醒
总结
生产者和消费者这是一个模型,完整的体现了线程的同步, Object 类的支持
菜鸡的Java笔记 生产者与消费者的更多相关文章
- 菜鸡的Java笔记 - java 断言
断言:assert (了解) 所谓的断言指的是在程序编写的过程之中,确定代码执行到某行之后数据一定是某个期待的内容 范例:观察断言 public class Abnorma ...
- 菜鸡的Java笔记 - java 正则表达式
正则表达式 RegularExpression 了解正则表达式的好处 正则表达式的基础语法 正则表达式的具体操作 content (内容 ...
- 菜鸡的Java笔记 数字操作类
数字操作类 Math 类的使用 Random 类的使用 BigInteger 和 BigDecimal 类的使用 Math 是一 ...
- 菜鸡的Java笔记 - java 线程常用操作方法
线程常用操作方法 线程的命名操作,线程的休眠,线程的优先级 线程的所有操作方法几乎都在 Thread 类中定义好了 线程的命名和取得 ...
- 菜鸡的Java笔记 日期操作类
日期操作类 Date 类与 long 数据类型的转换 SimpleDateFormat 类的使用 Calendar 类的使用 如 ...
- 菜鸡的Java笔记 开发支持类库
开发支持类库 SupportClassLibrary 观察者设计模式的支持类库 content (内容) 什么是观察者设计模式呢? ...
- 菜鸡的Java笔记 简单JAVA 类的开发原则以及具体实现
/* 现在要求定义一个雇员信息类 在这个类之中包含有雇员编号 姓名 职位 基本工资 佣金等信息 对于此时给定要求实际上就是描述一类事物,而这样的程序类在在java之中可以将其称为简单java类 ...
- 菜鸡的Java笔记 - java 访问控制权限
java中四种访问控制权限的使用 内容 在java里面一共定义有四个权限,按照由小到大的顺序:private<defaule<prote ...
- 菜鸡的Java笔记 国际化程序实现原理
国际化程序实现原理 Lnternationalization 1. Locale 类的使用 2.国家化程序的实现,资源读取 所谓的国际化的程序 ...
随机推荐
- Winform 控件命名规范
前言 最近 Winform 项目做得比较多,控件命名规范上常用的能记住,但是有些总要查,写个记录吧.方便以后自己用,大家也可以参考. 标准控件 序号 控件类型简写 控件类型 1 btn Button ...
- Javascript设计模式之原型模式、发布订阅模式
原型模式 原型模式用于在创建对象时,通过共享某个对象原型的属性和方法,从而达到提高性能.降低内存占用.代码复用的效果. 示例一 function Person(name) { this.name = ...
- Python代码阅读(第10篇):随机打乱列表元素
本篇阅读的代码实现了随机打乱列表元素的功能,将原有列表乱序排列,并返回一个新的列表(不改变原有列表的顺序). 本篇阅读的代码片段来自于30-seconds-of-python. shuffle fro ...
- 5 大场景深度探讨何为 Serverless 架构模式?
作者 | Hongqi 阿里云高级技术专家 究竟什么是 Serverless 架构? 什么是 Serverless 架构?按照 CNCF 对 Serverless 计算的定义,Serverless 架 ...
- 零基础怎么学Java?Java的运行机制是什么?Java入门基础!
Java语言是当前流行的一种程序设计语言,因其安全性.平台无关性.性能优异等特点,受到广大编程爱好者的喜爱. 想学习Java语言的同学对于Java的运行机制是必须要了解的!! 计算机高级语言的类型主要 ...
- Initialize this repository with a README
在gitHub创建项目时,有很多小伙伴不太清楚 Initialize this repository with a README 在什么情况下需要勾选,在这里简单说明 如下: 1. 翻译 如果要导入现 ...
- Linux系统安装MySql5.7并通过sql脚本导入数据
为了下载到的MySQL版本和目标系统相互兼容,在开启之前,最好了解目标系统的相关信息. 查询系统版本: cat /etc/issue 查看系统位数 getconf LONG_BIT 选择MySQL 根 ...
- Spring的JDK动态代理如何实现的(源码解析)
前言 上一篇文章中提到了SpringAOP是如何决断使用哪种动态代理方式的,本文接上文讲解SpringAOP的JDK动态代理是如何实现的.SpringAOP的实现其实也是使用了Proxy和Invoca ...
- Redis 面试常见问题———缓存雪崩、缓存击穿以及缓存穿透
在开发中会面临缓存异常可能会出现三个问题,分别是缓存雪崩.缓存击穿和缓存穿透.这三个问题会导致大量请求从缓存转移到数据库,如果请求的并发量很大的话,就会导致数据库崩溃.所以在面试官也会经常问这些问题. ...
- Go语言核心36讲(Go语言进阶技术二)--学习笔记
08 | container包中的那些容器 我们在上次讨论了数组和切片,当我们提到数组的时候,往往会想起链表.那么 Go 语言的链表是什么样的呢? Go 语言的链表实现在标准库的container/l ...