计数器与阻塞队列

news2024/11/19 15:31:27

目录

一:阻塞队列

模拟阻塞队列

二:线程池:

三:计数器:

定时器模拟实现


一:阻塞队列

阻塞队列是在原有的普通队列上做了扩充,标准库中原有的队列和子类都是线程不安全的。

1.线程安全

2.具有阻塞性

如果队列为空,进行出队列操作就会发生阻塞,直到有线程进入,对列不为空为止。

如果队列为满,进行入队列操作就会发生阻塞,直到对列不为空为止。

基于阻塞队列,最大的应用场景就是“生产者消费者模型”

生产者消费者模型有两大好处:

1.服务器之间的解耦合

2.起到削峰填谷的作用,在遇到请求量激增的情况下,可以保护下游服务器不会被请求冲垮。

代价:

1.需要更多机器来部署这样的消息队列

2.服务器之间的通信延迟会变长


模拟阻塞队列
//基于String进行存储,不考虑泛型
    class MyBlockingQueue{
        private String[] data = null;
        private volatile int head = 0;
        private volatile int tail = 0;
        private volatile int size = 0;

        private static Object lovker = new Object();

        public MyBlockingQueue(int capacity){
            data = new String[capacity];
        }

        public void put (String s) throws InterruptedException {
            synchronized (lovker) {
                if (size == data.length) {
                    while (size == data.length) {
                        lovker.wait();
                    }
                    data[tail] = s;
                    tail++;
                    if (tail >= data.length) {
                        tail = 0;
                    }
                    size++;
                    lovker.notify();
                }
            }
        }
            public String take() throws InterruptedException {
                String ret = " ";
                synchronized (lovker){
                    if(size == 0){
                        while (size == 0){
                            lovker.wait();
                        }
                        ret = data[head];
                        head++;
                        if(head >= data.length){
                            head = 0;
                        }
                        size--;
                        lovker.notify();
                    }
                    return ret;
                }
            }
二:线程池:

线程池的本质思想就是提高效率的。如果没有线程池,每次创建一个线程或者销毁一个线程都要调动操作系统内部的API,随着业务上对性能要求越来越高,线程的创建/销毁的频次越来越多,线程的开销就越来越大,无法再忽略不计了。

线程池就是解决上述问题的方案,线程池就是把线程提前从操作系统内部申请好,放到一个地方。后面需要使用线程,就直接从这个地方来取,而不是重新从操作系统中申请。同样的线程用完了也是回到这个地方。

为什么事先创建好线程放在那里已被待用更高效呢?

因为我们自己在编译器上写的代码属于用户态代码,由操作系统调用API创建线程是内核态的。

举个例子:

在银行办理事务时,如果在柜台的需要打印一张临时复印件,这个时候可以选择自己去门口复印,也可以让银行柜台内工作人员给你打印。

如果我自己去打印目的明确效率很高,整个过程连贯可控高效。

但是要柜台人员给我复印就会慢一些,因为在给我复印的时候有可能会有领导来给他布置任务,这时他可能会把我的事情滞后,先去完成领导布置的任务。同样的道理,在代码中也是一样,内核中的工作都是非常繁忙的,提交给内核的代码都是不可控的,效率往往比较低下。


标准库的线程池:java.util.concurrent

图中第四种最复杂,所以下面详细介绍第四种

在java里面线程分为两种,一种是核心线程(最少有多少个线程),一种是非核心线程(在线程扩容过程中新增的)两者之和为最大线程数

1.int corePoolSize:核心线程数

   int maximuumPoolSize:最大线程数

此线程可以支持“线程扩容”,某个线程初始情况下有M个线程,后来不够用,会再扩容N个线程。扩容的是非核心线程。

2.long keepAliveTime:线程存活的最大时间。

   TimeUnit unit :存活的时间的单位。

3.BlockingQueue<Runnable> workQueue:工作队列。

Runnable代表要执行的任务,通过submit把要执行的任务设置到线程池内部的工作队列中。

Runnable接口本身要执行的含义就是一段可以执行的任务。

4.ThreadFactory threadFactor:线程工厂

工厂是指工厂设计模式,是在创建类的实例时使用的设计模式,是Thread类的工厂类,通过这个类完成Thread的实例创建和初始化操作。此处的ThreadFactor可以针对线程池里的线程进行批量操作。

为什么会有这个工厂?

因为多个版本的构造方法类名必须要保持一致,必须要重载

如果在直角坐标系下定义一个类

class Point{

public Point(double x,double y){.....} // 用于平面直角坐标系

public Point(double r,double a){......} //用于极坐标系

这俩方法无法构成重载,所以工厂模式就填补了上述的局限性。

5.RejectedExecutionHandler hander:拒绝策略 这是上述所有参数中最复杂的最重要的

如果队列元素满了,不要阻塞而是要明确拒绝线程

java标准库给出了四种不同的拒绝策略


ThreadPoolExecutor (定制性更强,用起来麻烦)线程池功能非常强大,标准库对其封装拉一下,Executors(定制性弱,用起来方便)提供了一些工厂方法,可以更方便的构造出线程池。线程池创建的都是前台线程。

优点:

  1. 降低资源消耗:减少线程的创建和销毁带来的性能开销。
  2. 提高响应速度:当任务来时可以直接使用,不用等待线程创建
  3. 可管理性: 进行统一的分配,监控,避免大量的线程间因互相抢占系统资源导致的阻塞现象。

三:计数器:

相当于闹钟,在代码中往往需要程序定时执行,在java标准库中提供了定时器。


定时器模拟实现

除啦基于优先级队列的方式除外还有“时间轮”,这里只介绍优先级队列。

定时器这个东西很重要,非常实用,在后端开发中和“阻塞队列”类似,都会有专门的服务器。

用来在分布式系统中实现定时器这样的效果。

1.要有一个类,描述要执行的任务(任务的时间,内容逻辑)

2.管理多个任务,通过一定数据结构,把多个任务存起来

3.有专门的线程,执行任务

class MyTimerTask implements Comparable<MyTimerTask> {
    private Runnable runnable;
    // 此处这里的 time, 通过毫秒时间戳, 表示这个任务具体啥时候执行.
    private long time;

    public MyTimerTask(Runnable runnable, long delay) {
        this.runnable = runnable;
        this.time = System.currentTimeMillis() + delay;
    }

    public void run() {
        runnable.run();
    }

    public long getTime() {
        return time;
    }

    @Override
    public int compareTo(MyTimerTask o) {
        // 此处这里的 - 的顺序, 就决定了这里是大堆还是小堆.
        // 此处需要小堆.
        // 这里是谁减谁, 不要背. 可以先写成一种顺序, 试试.
        return (int) (this.time - o.time);
        // return (int) (o.time - this.time);
    }
}

class MyTimer {
    private PriorityQueue<MyTimerTask> queue = new PriorityQueue<>();
    private Object locker = new Object();

    public MyTimer() {
        // 创建线程, 负责执行上述队列中的内容
        Thread t = new Thread(() -> {
            try {
                while (true) {
                    synchronized (locker) {
                        while (queue.isEmpty()) {
                            locker.wait();
                        }
                        MyTimerTask current = queue.peek();
                        // 比如, 当前时间是 10:30, 任务时间是 12:00, 不应该执行.
                        // 如果当前时间是 10:30, 任务时间是 10:29, 应该执行
                        if (System.currentTimeMillis() >= current.getTime()) {
                            // 要执行任务
                            current.run();
                            // 把执行过的任务, 从队列中删除.
                            queue.poll();
                        } else {
                            // 先不执行任务
                            locker.wait(current.getTime() - System.currentTimeMillis());
                            // Thread.sleep(current.getTime() - System.currentTimeMillis());
                        }
                    }
                }
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        });
        t.start();
    }

    public void schedule(Runnable runnable, long delay) {
        synchronized (locker) {
            MyTimerTask myTimerTask = new MyTimerTask(runnable, delay);
            queue.offer(myTimerTask);
            locker.notify();
        }
    }
}

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

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

相关文章

OSMDroidOfflineDemo源码调试记录

文章目录 源码下载环境配置尝试不同离线加载遇到的问题 尝试安卓端加载离线地图&#xff0c;下载了使用osmdroid的离线版项目源码&#xff0c;更改JDK环境、gradle环境&#xff0c;一顿操作下来&#xff0c;踉踉跄跄的把程序跑起来了&#xff0c;但是离线的地图一直加载不出来。…

49.TFT_LCD液晶屏驱动设计与验证(2)

&#xff08;1&#xff09;Visio视图&#xff1a; &#xff08;2&#xff09;控制模块Verilog代码&#xff1a; module tft_ctrl(input clk_33M ,input reset_n ,input [23:0] data_in ,output [9:0] hang…

如何使用 SQLite ?

SQLite 是一个轻量级、嵌入式的关系型数据库管理系统&#xff08;RDBMS&#xff09;。它是一种 C 库&#xff0c;实现了自给自足、无服务器、零配置、事务性 SQL 数据库引擎。SQLite 的源代码是开放的&#xff0c;完全在公共领域。它被广泛用于各种应用程序&#xff0c;包括浏览…

Python | ValueError: could not convert string to float: ‘example’

Python | ValueError: could not convert string to float: ‘example’ 在Python编程中&#xff0c;类型转换是一个常见的操作。然而&#xff0c;当尝试将一个字符串转换为浮点数时&#xff0c;如果字符串的内容不是有效的浮点数表示&#xff0c;就会遇到“ValueError: could…

【python_将一个列表中的几个字典改成二维列表,并删除不需要的列】

def 将一个列表中的几个字典改成二维列表(original_list,headersToRemove_list):# 初始化一个列表用于存储遇到的键&#xff0c;保持顺序ordered_keys []# 遍历data中的每个字典&#xff0c;添加其键到ordered_keys&#xff0c;如果该键还未被添加for d in original_list:for …

SpringCloud之@FeignClient()注解的使用方式

FeignClient介绍 FeignClient 是 Spring Cloud 中用于声明一个 Feign 客户端的注解。由于SpringCloud采用分布式微服务架构&#xff0c;难免在各个子模块下存在模块方法互相调用的情况。比如订单服务要调用库存服务的方法&#xff0c;FeignClient()注解就是为了解决这个问题的…

Vim 文本编辑工具

Vim 基础命令 一、Vim 命令速查 Vim 是一款功能强大的文本编辑器&#xff0c;广泛应用于Linux系统中。以下是一些基础但非常有用的Vim命令&#xff0c;它们将帮助你更高效地使用Vim。 使用单个字母键通常需要进一步的输入以形成完整命令。特殊符号用来表示操作的位置。 命令…

Linux网络:传输层TCP协议(四)拥塞控制及延迟应答

目录 一、拥塞控制 二、延迟应答 一、拥塞控制 虽然 TCP 拥有滑动窗口这个大杀器机制来根据具体情况对发送的数据大小和速度进行实时控制, 能够高效并且可靠的发送大量的数据. 但是如果在双方建立好连接后的刚开始阶段就发送大量的数据。仍然可能引发一些问题. 因为同一个网…

【2024蓝桥杯/C++/A组/团建】

题目 代码 #include<bits/stdc.h> using namespace std;const int N 2e510;int a[N], b[N]; int ans; vector<int> Ga[N], Gb[N];void dfs(int ap, int af, int bp, int bf, int dep) {ans max(ans, dep);map<int, int> bk;for(auto ason : Ga[ap])if(aso…

免费【2024】springboot 程序设计基础视频学习系统的设计与实现

博主介绍&#xff1a;✌CSDN新星计划导师、Java领域优质创作者、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和学生毕业项目实战,高校老师/讲师/同行前辈交流✌ 技术范围&#xff1a;SpringBoot、Vue、SSM、HTML、Jsp、PHP、Nodejs、Python、爬虫、数据可视化…

CSS:mix-blend-mode属性(设置元素的混合模式)

目录 一、mix-blend-mode属性介绍 二、mix-blend-mode常用属性值 三、mix-blend-mode属性应用 四、文字智能适配背景 1、原始样式 2、添加混合 3、实现代码 一、mix-blend-mode属性介绍 CSS中的【mix-blend-mode属性】描述了元素的内容应该与元素的直系父元素的内容和…

nodejs安装及环境配置轨道交通运维检测系统App-OA人事办公排班故障维修

✌网站介绍&#xff1a;✌10年项目辅导经验、专注于计算机技术领域学生项目实战辅导。 ✌服务范围&#xff1a;Java(SpringBoo/SSM)、Python、PHP、Nodejs、爬虫、数据可视化、小程序、安卓app、大数据等设计与开发。 ✌服务内容&#xff1a;免费功能设计、免费提供开题答辩P…

【前端 09】JavaScript中的对象与JSON

JavaScript中的对象与JSON 在JavaScript中&#xff0c;对象和JSON&#xff08;JavaScript Object Notation&#xff09;是两个紧密相连但又有区别的概念。它们都在数据处理和交换中扮演着重要角色。本文将详细讲解JavaScript中的自定义对象以及JSON对象的基本概念、格式、用法…

赵本山:我跟你找游大队去,王平:实话告诉你,我就是游队长——小品《卖梨》(下)的台词与解说

赵本山&#xff1a;我跟你找游大队去&#xff0c;王平&#xff1a;实话告诉你&#xff0c;我就是游队长 ——小品《卖梨》&#xff08;下&#xff09;的台词与解说 &#xff08;接上&#xff09; 王平&#xff08;饰演警察&#xff09;&#xff1a;你少废话 你赶紧给我挪地方…

视觉SLAM第二讲

SLAM分为定位和建图两个问题。 定位问题 定位问题是通过传感器观测数据直接或间接求解位置和姿态。 通常可以分为两类&#xff1a;基于已知地图的定位和基于未知地图的定位。 基于已知地图的定位 利用预先构建的地图&#xff0c;结合传感器数据进行全局定位。SLAM中的全局…

USB 2.0 协议专栏之 USB 2.0 连接与枚举(二)

前言&#xff1a;本篇博客为手把手教学的 USB 2.0 协议栈类精品博客&#xff0c;该专栏博客侧重针对 USB 2.0 协议进行讲解。本篇博客将针对 USB 2.0 中的连接与枚举进行教学&#xff0c;USB 的枚举过程是 USB 协议中至关重要的一环&#xff0c;也是嵌入式工程师必须掌握的内容…

杂谈(杂鱼谈论c语言)——2.大小端字节序

⼤⼩端字节序和字节序判断 当我们了解了整数在内存中存储后&#xff0c;我们调试看⼀个细节&#xff1a; #include <stdio.h> int main() {int a 0x11223344;return 0; } 调试的时候&#xff0c;我们可以看到在a中的 0x11223344 这个数字是按照字节为单位&#xff0c;…

【多模态大模型】 ALBEF in NeurIPS 2021

一、引言 论文&#xff1a; Align before Fuse: Vision and Language Representation Learning with Momentum Distillation 作者&#xff1a; Salesforce Research 代码&#xff1a; ALBEF 特点&#xff1a; 该方法使用ViT进行图像特征提取&#xff0c;提出将BERT分两部分&am…

解密阿里大神写的天书般的Tree工具类,轻松搞定树结构!

首发公众号&#xff1a;赵侠客 一、引言 最近公司新进了不少新人&#xff0c;包括一些来自阿里、网易等大型企业的资深工程师。我们组的一位新同事是阿里来的专家&#xff0c;我在CR&#xff08;Code Review, 简称CR&#xff09;时看到了他编写的一个关于树操作的工具类&#…