阻塞队列与定时器

1.阻塞队列

1.1阻塞队列是什么

阻塞队列是一种特殊的队列. 也遵守 "先进先出" 的原则.

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

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

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

阻塞队列的一个典型应用场景就是 "生产者消费者模型". 这是一种非常典型的开发模型.

1.2生产者消费者模型

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

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

1) 阻塞队列就相当于一个缓冲区,平衡了生产者和消费者的处理能力.

比如在 "秒杀" 场景下, 服务器同一时刻可能会收到大量的支付请求. 如果直接处理这些支付请求,   服务器可能扛不住(每个支付请求的处理都需要比较复杂的流程).这个时候就可以把这些请求都放到一个阻塞队列中, 然后再由消费者线程慢慢的来处理每个支付请求.

这样做可以有效进行“削峰”,防止服务器被突然到来的一波请求直接冲垮

2) 阻塞队列也能使生产者和消费者之间 解耦.

意思就是消费者只管消费阻塞队列中的数据,而不关心生产这些数据的人是谁,怎么生产的。

1.3标准库中的阻塞队列

 Java 标准库中内置了阻塞队列. 如果我们需要在一些程序中使用阻塞队列, 直接使用标准库中的即可.

   BlockingQueue 是一个接口. 真正实现的类是 LinkedBlockingQueue.

   put 方法用于阻塞式的入队列, take方法用于阻塞式的出队列.

   BlockingQueue 也有 offer, poll, peek 等方法, 但是这些方法不带有阻塞特性.

BlockingQueue<String> queue = new LinkedBlockingQueue<>();
//入队列
queue.put("abc");
// 出队列 . 如果没有 put 直接 take, 就会阻塞 .
String elem = queue.take();

生产者消费者模型

package thread;import java.util.Random;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;public class ThreadDemo13 {public static void main(String[] args) throws InterruptedException {BlockingQueue<Integer> blockingQueue  = new LinkedBlockingQueue<>();Thread customer = new Thread(()->{while(true) {try {int key = blockingQueue.take();System.out.println(Thread.currentThread().getName()+ "消费:" + key);Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}},"消费者");Thread producer = new Thread(()-> {Random r = new Random();while(true){int x = r.nextInt(100);try {blockingQueue.put(x);System.out.println(Thread.currentThread().getName() +"生产:"+x);Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}},"生产者");customer.start();producer.start();customer.join();producer.join();}
}

1.4阻塞队列实现

  通过 "循环队列" 的方式来实现.

  使用 synchronized 进行加锁控制.

   put 插入元素的时候, 判定如果队列满了, 就进行 wait. (注意, 要在循环中进行 wait.被唤醒时不一 定队列就不满了, 因为同时可能是唤醒了多个线程). 

   take 取出元素的时候, 判定如果队列为空, 就进行 wait. (也是循环 wait)

public class MyBlockingQueue {private final String[] elem = new String[1000];private  int head = 0;private  int tail = 0;private  int size = 0;final Object locker = new Object();public void put(String key) throws InterruptedException {synchronized (locker) {// 此处最好使用 while.// 否则 notifyAll 的时候 , 该线程从 wait 中被唤醒 ,// 但是紧接着并未抢占到锁 . 当锁被抢占的时候 , 可能又已经队列满了 // 就只能继续等待while (size >= elem.length) {locker.wait();}elem[tail] = key;tail++;size++;if (tail >= elem.length) {tail = 0;}locker.notify();}}public String take() throws InterruptedException {synchronized (locker) {while (size == 0) {locker.wait();}String key = elem[head];head++;size--;locker.notify();return key;}}public static void main(String[] args) throws InterruptedException {MyBlockingQueue myBlockingQueue = new MyBlockingQueue();Thread producer = new Thread(()->{int count = 0;while(true){try {myBlockingQueue.put(count+"");System.out.println("生产者生产:"+count);count++;Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}});Thread customer = new Thread(()->{while(true){try {String res = myBlockingQueue.take();System.out.println("消费者消费:"+res);} catch (InterruptedException e) {e.printStackTrace();}}});producer.start();customer.start();}
}

2.定时器

2.1定时器是什么

定时器也是软件开发中的一个重要组件. 类似于一个 "". 达到一个设定的时间之后, 就执行某个指定好的代码,比如网络通信中, 如果对方 500ms 内没有返回数据, 则断开连接尝试重连.

比如一个 Map, 希望里面的某个 key 在 3s 之后过期(自动删除).类似于这样的场景就需要用到定时器.

2.2标准库中的定时器

标准库中提供了一个 Timer . Timer 类的核心方法为schedule包含两个参数. 第一个参数指定即将要执行的任务代码, 第二个参数指定多长时间之后 执行 (单位为毫秒).

Timer timer = new Timer();
timer.schdule(new TimeTask(){@Override   public void run(){System.out.println("hello");    }
},3000);

2.3实现定时器

一个带优先级的阻塞队列

为啥要带优先级呢?

因为阻塞队列中的任务都有各自的执行时刻 (delay).最先执行的任务一定是 delay 最小的. 使用带优先级的队列就可以高效的把这个 delay 最小的任务找出来.

队列中的每个元素是一个 Task 对象.

Task 中带有一个时间属性, 队首元素就是即将执行的元素

同时有一个 worker 线程一直扫描队首元素, 看队首元素是否需要执行

import java.util.PriorityQueue;// 创建一个类, 用来描述定时器中的一个任务
class MyTimerTask implements Comparable<MyTimerTask> {// 任务啥时候执行. 毫秒级的时间戳.private long time;// 任务具体是啥.private Runnable runnable;public MyTimerTask(Runnable runnable, long delay) {// delay 是一个相对的时间差. 形如 3000 这样的数值.// 构造 time 要根据当前系统时间和 delay 进行构造.time = System.currentTimeMillis() + delay;this.runnable = runnable;}public long getTime() {return time;}public Runnable getRunnable() {return runnable;}@Overridepublic int compareTo(MyTimerTask o) {// 认为时间小的, 优先级高. 最终时间最小的元素, 就会放到队首.// 怎么记忆, 这里是谁减去谁?? 不要记!! 记容易记错~~// 随便写一个顺序, 然后实验一下就行了.return (int) (this.time - o.time);// return (int) (o.time - this.time);}
}// 定时器类的本体
class MyTimer {// 使用优先级队列, 来保存上述的 N 个任务private PriorityQueue<MyTimerTask> queue = new PriorityQueue<>();// 用来加锁的对象private Object locker = new Object();// 定时器的核心方法, 就是把要执行的任务添加到队列中.public void schedule(Runnable runnable, long delay) {synchronized (locker) {MyTimerTask task = new MyTimerTask(runnable, delay);queue.offer(task);// 每次来新的任务, 都唤醒一下之前的扫描线程. 好让扫描线程根据最新的任务情况, 重新规划等待时间.locker.notify();}}// MyTimer 中还需要构造一个 "扫描线程", 一方面去负责监控队首元素是否到点了, 是否应该执行; 一方面当任务到点之后,// 就要调用这里的 Runnable 的 Run 方法来完成任务public MyTimer() {// 扫描线程Thread t = new Thread(() -> {while (true) {try {synchronized (locker) {while (queue.isEmpty()) {// 注意, 当前如果队列为空, 此时就不应该去取这里的元素.// 此处使用 wait 等待更合适. 如果使用 continue, 就会使这个线程 while 循环运行的飞快,// 也会陷入一个高频占用 cpu 的状态(忙等).locker.wait();}MyTimerTask task = queue.peek();long curTime = System.currentTimeMillis();if (curTime >= task.getTime()) {// 假设当前时间是 14:01, 任务时间是 14:00, 此时就意味着应该要执行这个任务了.// 需要执行任务.queue.poll();task.getRunnable().run();} else {// 让当前扫描线程休眠一下, 按照时间差来进行休眠.// Thread.sleep(task.getTime() - curTime);locker.wait(task.getTime() - curTime);}}} catch (InterruptedException e) {e.printStackTrace();}}});t.start();}
}// 写一个定时器
public class TimeTest {public static void main(String[] args) {MyTimer timer = new MyTimer();timer.schedule(new Runnable() {@Overridepublic void run() {System.out.println("hello 3");}}, 3000);timer.schedule(new Runnable() {@Overridepublic void run() {System.out.println("hello 2");}}, 2000);timer.schedule(new Runnable() {@Overridepublic void run() {System.out.println("hello 1");}}, 1000);System.out.println("程序开始运行");}
}

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

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

相关文章

在线旅游网站,基于 SpringBoot+Vue+MySQL 开发的前后端分离的在线旅游网站设计实现

目录 一. 前言 二. 功能模块 2.1. 登录界面 2.2. 管理员功能模块 2.3. 用户功能模块 三. 部分代码实现 四. 源码下载 一. 前言 随着科学技术的飞速发展&#xff0c;各行各业都在努力与现代先进技术接轨&#xff0c;通过科技手段提高自身的优势&#xff0c;旅游网站当然…

分布式与集群的区别

先说区别&#xff1a; 分布式是并联工作的&#xff0c;集群是串联工作的。 分布式中的每一个节点都可以做集群。而集群并不一定就是分布式的。 集群举例&#xff1a;比如新浪网&#xff0c;访问的人很多&#xff0c;他可以做一个集群&#xff0c;前面放一个相应的服务器&…

Covalent Network(CQT)通过 “新曙光” 计划实现重要里程碑,增强以太坊时光机,提供 30% 的年化质押收益率

Covalent Network&#xff08;CQT&#xff09;作为集成超过 280 条区块链&#xff0c;并服务于超过 2.8 亿个钱包的领先结构化数据基础设施层&#xff0c;宣布了其战略计划 “新曙光” 中的一个重要进展。随着网络升级并完成了准备工作的 75%&#xff0c;这将为即将部署的以太坊…

JUC下CountDownLatch详解

详细介绍 CountDownLatch是Java并发包java.util.concurrent中提供的一个同步工具类&#xff0c;它允许一个或多个线程等待其他线程完成操作后再继续执行。这个工具类基于一个计数器&#xff0c;计数器的初始值可以由构造函数设定。线程调用countDown()方法会将计数器减1&#x…

uniapp——弹出键盘遮挡住输入框 textarea,处理方法

案例 在写输入框的时候会遇见 键盘遮挡住部分textarea框的一部分&#xff0c;使用cursor-spacing处理即可 修改后&#xff1a; 其他问题&#xff1a; 调起键盘输入时&#xff0c;不希望上方的内容被顶上去 代码 <view class"commentBox" :style"botto…

爬虫学习(4)每日一笑

代码 import requests import re import osif __name__ "__main__":if not os.path.exists("./haha"):os.makedirs(./haha)url https://mlol.qt.qq.com/go/mlol_news/varcache_article?docid6321992422382570537&gameid3&zoneplat&webview…

Linux 认识与学习Bash——3

在Linux bash中&#xff0c;数据流重定向是指将命令的输出从默认的标准输出&#xff08;通常是终端&#xff09;重定向到其他位置&#xff0c;如文件或另一个命令的输入。这是通过使用特定的符号来实现的。例如&#xff0c;>用于将输出重定向到文件&#xff0c;而<用于将…

Proxmox VE 8 用SDN隔离用户网络

作者&#xff1a;田逸&#xff08;formyz&#xff09; 最新发布的Proxmox VE&#xff08;以下简称PVE&#xff09; 8在Web管理后台集成了易于操作的SDN&#xff08;软件定义网络&#xff09;功能插件&#xff0c;其实质是对不同的PVE用户指定不同的网络&#xff0c;进行逻辑隔离…

D3.js实战:数据可视化高级技巧实例应用

基础 首先&#xff0c;我们需要一个HTML文件来引入D3.js库&#xff0c;并准备一个画布来放置我们的图表。 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"width…

[移动通讯]【无线感知-P1】[从菲涅尔区模型到CSI模型-3][Mobius transformations-7】【Inversion】

前言&#xff1a; mobius map 里面比较难的是inversion &#xff0c;林菲尔德学院&#xff08;Linfield College&#xff09; Michael P. Hitchman. 有本书详细介绍一下该方向的一些原理&#xff0c;例子. Whitman College Book: 《Geometry with an Introduction to Cosmic T…

el-select选项框内容过长

利用popper-class实现选项框内容过长&#xff0c;截取显示功能&#xff1a; <el-select popper-class"popper-class" :popper-append-to-body"false" v-model"value" placeholder"请选择"><el-optionv-for"item in opt…

js 字符串截取,截取指定字符前面/后面的字符串

一个符号截取&#xff1a; let str hello, how are you doing? username! // 截取 ? 前面的字符串&#xff1a; let before1 str.substring(0,str.lastIndexOf("?")) // 不包含 ? console.log(before1,before1---------------); // hello, how are you doing …

Java使用httpclient发送POST请求

声明&#xff1a;实习时接到一个送餐机器人项目 涉及到httpclient 1.创建请求对象:post或者get HttpPost httpPost new HttpPost(url); 2. 创建httpclient对象 CloseableHttpClient httpclient HttpClientBuilder.create().build(); 3. 创建请求头对象 BasicResponseHa…

初识 Linux线程

再学习完Linux进程后,本期,我们来讲解Linux线程 1.为什么需要线程 在之前学习进程前,我们写的所有代码几乎都是单个执行流的,也就是说我们的代码只有一条路走. 在学习进程后,我们可以通过fork进行进程创建,给进程分配任务进行多执行流执行任务,问题来了 那我们为什么还需要…

使用torch.nn.Sequential构建神经网络

torch.nn.Sequential 是 PyTorch 中的一个非常有用的类&#xff0c;它允许用户以一种简单和直观的方式构建神经网络。Sequential 容器可以包含多个神经网络层&#xff0c;这些层会按照它们被添加到 Sequential 中的顺序依次执行。 1.关键特性 以下是 torch.nn.Sequential 的一…

Lenet5硬件加速RTL - 06(nnLinear)

timescale 1ns / 1ps// Description : 全连接层 // Change Logs : 2024.05.10 - Yang.Long - 1.0.0 - module nnLinear #(parameter G_WDEPTH 12 ,//权重深度parameter G_PDEPTH 8 ,//像素深度parameter G_LINEXLEN 160 ,//每行图像宽度pa…

清空回收站是彻底删除吗?一文解答你的疑问!

“我刚刚本来想在回收站中恢复一个文件的&#xff0c;但是一不小心就清空了回收站&#xff0c;想问问清空回收站是彻底删除吗&#xff1f;清空了回收站文件还有机会找回吗&#xff1f;” 在使用电脑的过程中&#xff0c;我们经常会将不再需要的文件或文件夹移动到回收站&#x…

数据结构与算法学习笔记六-二叉树的顺序存储表示法和实现(C语言)

目录 前言 1.数组和结构体相关的一些知识 1.数组 2.结构体数组 3.递归遍历数组 2.二叉树的顺序存储表示法和实现 1.定义 2.初始化 3.先序遍历二叉树 4.中序遍历二叉树 5.后序遍历二叉树 6.完整代码 前言 二叉树的非递归的表示和实现。 1.数组和结构体相关的一些知…

AUTOSAR OS调度表讲解

调度表 AUTOSAR OS通过调度表(Schedule Table)来解决一个alarm只能激活一个任务的限制。调度表是预定义的行为序列,通过到期点实现。AUTOSAR OS遍历调度表并依次处理每个到期点,遍历由底层的counter来实现驱动。 到期点发生在从概念零开始的静态配置偏移量上。偏移量在静…

【程序设计和c语言-谭浩强配套】(适合专升本、考研)

一晃大半年没更新了&#xff0c;这一年一直在备考&#xff0c;想着这几天把前段时间学的c语言给大家分享一下&#xff0c;在此做了一个专栏&#xff0c;有需要的小伙伴可私信获取o。 简介&#xff1a;本专栏所有内容皆适合专升本、考研的复习资料&#xff0c;本人手上也有日常…