JAVAEE初阶相关内容第八弹--多线程(初阶)

本文目录

阻塞队列

阻塞队列是什么?

标准库中的阻塞队列

生产者消费者模型

阻塞队列的实现

普通队列实现:

入队列:

出队列:

完整代码:

 加阻塞

加锁

加阻塞


阻塞队列

队列:先进先出,实际上还有一些特殊的队列,不一定非得遵守先进先出。

例如:优先级队列。PriorityQueue

阻塞队列也是特殊的队列,虽然也是先进先出的,但是带有特殊的功能。

消息队列也是特殊的队列,相当于是在阻塞队列的基础上,加上个“消息的类型”按照制定的类别进行先进先出。

阻塞队列是什么?

阻塞队列是一种特殊的队列,也遵守“先进先出”的原则。

阻塞队列可以是一种线程安全的数据结构,并且具有以下的特征:

(1)当队列满时,继续入队列就会阻塞,直到有其他线程从队列中取走元素。

(2)当队列空的时候,继续出队列也会阻塞,直到有其他线程往队列里插入元素。

标准库中的阻塞队列

阻塞队列的一个典型的应用场景就是“生产者消费者模型”。

生产者消费者模式就是通过一个容器来解决生产者和消费者的强耦合问题。

生产者消费者彼此之间不直接通讯,而是通过阻塞队列来进行通讯,所以生产者生产完数据不需要等待消费者处理,直接扔给阻塞队列,消费者不找生产者要数据,而是直接从阻塞队列中取。

生产者消费者模型给我们带来了两个很重要的好处:

1.实现了发送方和接收方的解耦。(降低耦合的过程就叫解耦)

2.可以做到“削峰填谷”,保证了系统的稳定性。

代码实现要求:首先会使用标准库提供的阻塞队列。其次需要自己实现一个简单的阻塞队列。

Queue提供的方法有三个:

1.入队列 offer

2.出队列 poll

3.取队首元素 peek

阻塞队列主要是两个:[带有阻塞功能的]

1.入队列 put

2.出队列 take

阻塞队列代码:

public class ThreadD21 {public static void main(String[] args) throws InterruptedException {BlockingQueue<String> blockingQueue = new LinkedBlockingQueue<>();blockingQueue.put("hi");String s = blockingQueue.take();System.out.println(s);}
}

生产者消费者模型

public class ThreadD22 {public static void main(String[] args) {BlockingQueue<Integer> blockingQueue = new LinkedBlockingQueue<>();//创建两个线程//消费者线程Thread customer = new Thread(() ->{while (true){try {Integer result = blockingQueue.take();System.out.println("消费元素" +result);} catch (InterruptedException e) {throw new RuntimeException(e);}}});customer.start();Thread producer = new Thread(() ->{int count = 0;while(true){try {blockingQueue.put(count);System.out.println("生产元素 " + count);count++;Thread.sleep(500);} catch (InterruptedException e) {throw new RuntimeException(e);}}});producer.start();}
}

运行代码结果:

阻塞队列的实现

普通队列实现:

实现阻塞队列,首先要实现普通队列,构建一个类,名为MyBlockingQueue1,在这个类中写出入队列和出队列的两个操作。

首先定义四个变量。定义一个数组,数组的长度,队首标记和队尾标记。

   private int[] items= new int[1000];private int tail = 0;private int head = 0;private int size = 0;
入队列:

传值进入,进行条件判断

如果元素个数与数组的长度相同,这时队满,没空插入新元素,进行返回。

将想入队列的元素赋值给尾标记的位置,将尾标记向后加加指向后一个元素。

如果尾标记指向的位置大于或者等于数组的长度,则将尾标记置为0。

最后数组进行size++操作。

迷糊点:items.length与size的区别!一个是系统就给这个数组分配了这么多的存储空间,而size是实际上数组这里存储的长度。

还要注意tali++的位置!

//入队列操作public void put(int value){if(size == items.length){return;}items[tail] = value;tail ++;if(tail >= items.length ){tail = 0;}size++;}
出队列:

首先进行判断,假如队列为空,size等于0,则不能出队列,返回。

如果头标记的位置大于等于整个数组的长度,则需要将头标记位记为0.

进行修改,设置一个变量result,将头标记指向的元素存到result中,返回。

head标记为进行向后挪动,数组里真正的元素少1所以进行size--操作。

注意代码执行的位置!!

//出队列操作
public Integer take(){if(size == 0){return null;}int result =items[head];head++;if(head >= items.length){head = 0;}size--;return result;}
完整代码:

在主函数中需要注意的点:

拿出元素queue.take()

放入元素queue.put(value)

class MyBlockingQueue1{private int[] items= new int[1000];private int tail = 0;private int head = 0;private int size = 0;//入队列public void put(int value){if(size == items.length){return;}items[tail] = value;tail ++;if(tail >= items.length ){tail = 0;}size++;}//出队列public Integer take(){if(size == 0){return null;}int result =items[head];head++;if(head >= items.length){head = 0;}size--;return result;}
}
public class ThreadD23 {public static void main(String[] args) throws InterruptedException {MyBlockingQueue queue = new MyBlockingQueue();queue.put(1);queue.put(2);queue.put(3);queue.put(4);int result = queue.take();System.out.println("result = "+result);result = queue.take();System.out.println("result = "+result);result = queue.take();System.out.println("result = "+result);result = queue.take();System.out.println("result = "+result);}
}

 加阻塞

需要注意的是,这里加阻塞功能就以为着程序是要在多线程条件下。要想实现阻塞功能,首先要保证需要实现线程安全。

加锁

首先为了保证线程安全,就需要将代码上锁,如图:

加阻塞

自己版本的阻塞队列最终版代码实现:

class MyBlockingQueue1{private int[] items= new int[1000];private int tail = 0;private int head = 0;private int size = 0;//入队列public void put(int value) throws InterruptedException {synchronized (this) {while(size == items.length){//return;this.wait();}items[tail] = value;tail ++;if(tail >= items.length ){tail = 0;}//这个notify唤醒的是take的waitthis.notify();size++;}}//出队列public Integer take() throws InterruptedException {int result = 0;synchronized (this) {while(size == 0){// return null;this.wait();}result =items[head];head++;if(head >= items.length){head = 0;}size--;//唤醒put中的wait;this.notify();}return result;}
}
public class ThreadD23 {public static void main(String[] args) throws InterruptedException {MyBlockingQueue queue = new MyBlockingQueue();queue.put(1);queue.put(2);queue.put(3);queue.put(4);int result = queue.take();System.out.println("result = "+result);result = queue.take();System.out.println("result = "+result);result = queue.take();System.out.println("result = "+result);result = queue.take();System.out.println("result = "+result);}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/107381.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Python基于Flask的招聘信息爬取,招聘岗位分析、招聘可视化系统

Python基于Flask招聘信息可视化系统 一、介绍 今天为大家带来的是Python基于Flask的招聘信息爬取&#xff0c;招聘岗位分析、招聘可视化系统。视频演示地址&#xff1a;https://www.bilibili.com/video/BV1Tp4y1A7nW/ Python基于flask的招聘数据可视化&#xff0c;招聘岗位分…

不关闭Tamper Protection(篡改保护)下强制卸载Windows Defender和安全中心所有组件

个人博客: xzajyjs.cn 背景介绍 由于微软不再更新arm版本的win10系统&#xff0c;因此只能通过安装insider preview的镜像来使用。而能找到的win10 on arm最新版镜像在安装之后由于内核版本过期&#xff0c;无法打开Windows安全中心面板了&#xff0c;提示如下&#xff1a; 尝…

常用百宝箱——日志处理

目录 前言 一、logging库 二、logging日志等级 三、logging四大组件 四、封装示例 总结 前言 日志是记录特定时间段或事件的详细信息的文件或记录。它们通过时间戳和关键词或描述符来标识事件或行动。日志可以用于许多目的&#xff0c;例如&#xff1a;故障排除、网络安全…

小白备战大厂算法笔试(三)——栈、队列、双向队列

文章目录 栈栈常用操作栈的实现基于链表的实现基于数组的实现 两种实现对比栈典型应用 队列队列常用操作队列实现基于链表的实现基于数组的实现 队列典型应用 双向队列双向队列常用操作双向队列实现基于双向链表的实现基于数组的实现 双向队列应用 栈 栈是一种遵循先入后出的逻…

solidworks底部状态栏显示不出来

如下图所示&#xff0c;solidworks主界面下面的状态栏突然不见了。 怎么调出来&#xff1f; 第一步&#xff1a;点击视图菜单&#xff0c;用户界面&#xff0c;把状态栏前的勾勾上。 第二步&#xff1a;把视图下面的触摸模式关掉&#xff0c;这一点很容易被大家忽略。

天津web前端培训班 前端是否适合零基础学?

随着HTML 5和ECMAScript 6的正式发布&#xff0c;大量的前端业务逻辑&#xff0c;极大地增加了前端的代码量&#xff0c;前端代码的模块化、按需加载和依赖管理势在必行&#xff0c;因此Web前端越来越被人们重视。 Web前端的就业前景 Web前端开发工程师薪资持续走高&#xff…

基于订单流的日内盘口策略

量化策略开发&#xff0c;高质量社群&#xff0c;交易思路分享等相关内容 『正文』 ˇ 目录 一、基于订单流的日内盘口策略 二、亚努斯量化交易图表介绍 一、基于订单流的日内盘口策略 大家好&#xff0c;我是乌克兰剑圣。 在7月份的时候我们发布了第一版盘口策略专享策…

【python零基础入门学习】python基础篇之系统模块调用shell命令执行(四)

本站以分享各种运维经验和运维所需要的技能为主 《python》&#xff1a;python零基础入门学习 《shell》&#xff1a;shell学习 《terraform》持续更新中&#xff1a;terraform_Aws学习零基础入门到最佳实战 《k8》暂未更新 《docker学习》暂未更新 《ceph学习》ceph日常问题解…

Redis 初识与入门

1. 什么是Redis Redis 是一种基于内存的数据库&#xff0c;对数据的读写操作都是在内存中完成&#xff0c;因此读写速度非常快&#xff0c;常用于缓存&#xff0c;消息队列、分布式锁等场景。 Redis 提供了多种数据类型来支持不同的业务场景&#xff0c;比如 String(字符串)、…

论文阅读 (100):Simple Black-box Adversarial Attacks (2019ICML)

文章目录 1 概述1.1 要点1.2 代码1.3 引用 2 背景2.1 目标与非目标攻击2.2 最小化损失2.3 白盒威胁模型2.4 黑盒威胁模型 3 简单黑盒攻击3.1 算法3.2 Cartesian基3.3 离散余弦基3.4 一般基3.5 学习率 ϵ \epsilon ϵ3.6 预算 1 概述 1.1 要点 题目&#xff1a;简单黑盒对抗攻…

mysql在ubuntu上命令行登陆密码不正确

1.登陆提示如下 2.使用mysql -u root -p登录也是类似的 3.打开宝塔面板 点击root密码&#xff0c;更改密码后即可在命令行界面登录 4.登录效果如下

大数据Flink(七十八):SQL 的水印操作(Watermark)

文章目录 SQL 的水印操作(Watermark) 一、为什么要有 WaterMark