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

news2024/12/26 11:21:39

本文目录

阻塞队列

阻塞队列是什么?

标准库中的阻塞队列

生产者消费者模型

阻塞队列的实现

普通队列实现:

入队列:

出队列:

完整代码:

 加阻塞

加锁

加阻塞


阻塞队列

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

例如:优先级队列。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的wait
            this.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.coloradmin.cn/o/1002196.html

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

相关文章

福立转债,科数转债上市价格预测

福立转债118043 基本信息 转债名称&#xff1a;福立转债&#xff0c;评级&#xff1a;AA-&#xff0c;发行规模&#xff1a;7.0亿元。 正股名称&#xff1a;福立旺&#xff0c;今日收盘价&#xff1a;20.07元&#xff0c;转股价格&#xff1a;21.28元。 当前转股价值 转债面值…

Java—JDK8新特性—重复注解

目录 重复注解是什么&#xff1f; 常见的重复注解的应用场景 源码&#xff08;JDK中哪里&#xff1f;&#xff09; 在实际开发中哪里使用了注解&#xff08;举例&#xff09; 使用步骤 案例 重复注解是什么&#xff1f; 重复注解&#xff0c;一个注解可以在一个类、方法、…

限流式保护器在古建筑消防安全中的应用

安科瑞 华楠 【摘要】我国古建筑众多且具有自身的消防安全特性。本文结合当前古建筑消防安全形势从消防管理、防火设计及火灾扑救三个方面对我国古建筑消防安全问题进行系统的阐述并提出相关对策。 【关键词】古建筑&#xff1b;消防安全&#xff1b;电气防火&#xff1b;限流…

C++ String类的简单实现(非模板)

头文件 #ifndef MY_STRING_H #define MY_STRING_Hclass Mstring {private:int m_length;char* m_pointer;public://构造函数Mstring();//有参构造Mstring(const char* str);//拷贝构造Mstring(const Mstring& obj);//获取长度int length()const;//转换为C字符串const char…

sprinboot 引入 Elasticsearch 依赖包

1.springboot与es的版本有比较强的绑定关系&#xff0c;如果springboot工程引入es的依赖后报一些依赖的错误&#xff0c;那么就看表格中的对应关系&#xff0c;将sprinboot或者es的版本做对应的调整 2.本人是从springboot1.x升级到springboot2.x&#xff0c;做了排包工作 3.升级…

在海外如何通过App Store本地化提高下载量

随着应用市场的应用持续增长&#xff0c;越来越多的应用和游戏占据了全球排行榜的主导地位。因此本地化应用程序商店的展示&#xff0c;对于吸引更多用户并在当今的市场中竞争至关重要。应用程序本地化不仅仅包括简单的翻译&#xff0c;还需要处理内容本身。 1、针对客户使用的…

HummerRisk V1.4.1 发布

HummerRisk V1.4.1发布&#xff1a; 大家好&#xff0c;增加检测整合报告下载&#xff0c;定制多云整合报告并下载PDF&#xff0c;增加K8s 检测规则组&#xff0c;Kubernetes、Rancher、KubeSphere 检测规则组以及规则。新增云账号管理页面关联菜单&#xff0c;新增资源同步日…

C++QT day4

仿照string类&#xff0c;完成myString类 #include <iostream> #include <cstring> using namespace std; class myString {private:char *str; //记录c风格的字符串int size; //记录字符串的实际长度public://无参构造myString():size(10){s…

【力扣每日一题】2023.9.11 课程表Ⅲ

目录 题目&#xff1a; 示例&#xff1a; 分析&#xff1a; 代码&#xff1a; 题目&#xff1a; 示例&#xff1a; 分析&#xff1a; 今天是和前两天一样课程表&#xff0c;不过今天不太一样了&#xff0c;今天不是图论了&#xff0c;可以看作是全新的题目。 给我们一个课…

Java——选择语句

if语句 语法格式&#xff1a; if(表达式){若干语句 } 例&#xff1a; ​import java.util.Scanner; public class Test1 {public static void main(String[] args) {Scanner sc new Scanner(System.in);int n sc.nextInt();if(n%20){System.out.println("even");}…

Linux命令200例:write用于向特定用户或特定终端发送信息

&#x1f3c6;作者简介&#xff0c;黑夜开发者&#xff0c;CSDN领军人物&#xff0c;全栈领域优质创作者✌。CSDN专家博主&#xff0c;阿里云社区专家博主&#xff0c;2023年6月csdn上海赛道top4。 &#x1f3c6;数年电商行业从业经验&#xff0c;历任核心研发工程师&#xff0…

v2.0舆情分析系统整体功能介绍

登录界面&#xff1a; 1、输入用户名、密码后可正常登录&#xff1b; 2、注册功能点击后进入注册界面正常注册用户信息&#xff1b; 3、忘记密码功能&#xff1a;点击后进入忘记密码页面demo 登录后&#xff0c;进入主页面&#xff0c;主页面目前包含关键词输入功能&#xf…

如何注册哥伦比亚商标?

在当今全球市场竞争激烈的环境下&#xff0c;商标已成为企业的重要资产之一。哥伦比亚作为一个拥有丰富历史文化的国家&#xff0c;对知识产权保护的重视程度也在逐渐提高。通过在哥伦比亚注册商标&#xff0c;企业可以更好地拓展南美市场&#xff0c;并进一步提升品牌价值和知…

Selenium自动化测试 —— 通过cookie绕过验证码的操作

验证码的处理 对于web应用&#xff0c;很多地方比如登录、发帖都需要输入验证码&#xff0c;类型也多种多样&#xff1b;登录/核心操作过程中&#xff0c;系统会产生随机的验证码图片&#xff0c;进行验证才能进行后续操作 解决验证码的方法如下&#xff1a; 1、开发做个万能…

T2I-Adapter:增强文本到图像生成的控制能力

链接&#xff1a;GitHub - TencentARC/T2I-Adapter: T2I-Adapter 文本到图像生成 (T2I) 是人工智能领域的一个重要研究方向。近年来&#xff0c;随着深度学习技术的发展&#xff0c;T2I 技术取得了显著进展&#xff0c;生成的图像在视觉效果上已经与真实图像难以区分。 然而&…

C++ -day3

1、自行封装一个栈的类 头文件 #ifndef ZY1_H #define ZY1_H#include <iostream>using namespace std;class Stack { private:int *p nullptr; //存储栈的数组int top; //栈顶元素的下标int max;public://定义析构函数~Stack();//定义拷贝构造函数…

零基础教程:使用yolov8训练无人机VisDrone数据集

1.准备数据集 1.先给出VisDrone2019数据集的下载地址&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1e2Q0NgNT-H-Acb2H0Cx8sg 提取码&#xff1a;31dl 2.将数据集VisDrone放在datasets目录下面 2.数据集转换程序 1.在根目录下面新建一个.py文件&#xff0c;取名叫…

全开源影视APP源码带后台 苍穹影视APP源码 免受权带安装教程

苍穹影视 V20 全新后台七彩视界免受权开源源码此版本为天穹公益版开源无解密安装教程 全新后台很是都雅,源码全开源无加密。 PC 端对接教程&#xff1a; 建议在浮图下操作 正常安装前后端 然后安装米酷 cms 根据教程安装即可 米酷 cms 对接部门已被我改动&#xff0c;只要在安装…

【C++进阶】二叉树进阶之二叉搜索树

&#x1f466;个人主页&#xff1a;Weraphael ✍&#x1f3fb;作者简介&#xff1a;目前学习C和算法 ✈️专栏&#xff1a;C航路 &#x1f40b; 希望大家多多支持&#xff0c;咱一起进步&#xff01;&#x1f601; 如果文章对你有帮助的话 欢迎 评论&#x1f4ac; 点赞&#x1…