生产者,消费者,队列缓冲区,线程

public class CustomQueue {
    private BlockingQueue<Integer> queue;

    public CustomQueue() {
        // 初始化一个容量为1的阻塞队列
        queue = new LinkedBlockingQueue<>(1);
    }

    public void put(int num) throws InterruptedException {
        // 将数字放入队列
        queue.put(num);
    }

    public int get() throws InterruptedException {
        // 从队列中获取数字,如果队列为空,则阻塞等待
        return queue.take();
    }




    public static void main(String[] args) {
        CustomQueue customQueue = new CustomQueue();

        // 生产者线程,向队列中放入数字
        Thread producerThread = new Thread(() -> {
            try {
                customQueue.put(1);
                System.out.println("Put 1 into the queue");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

        // 消费者线程,从队列中获取数字
        Thread consumerThread = new Thread(() -> {
            try {
                int num = customQueue.get();
                System.out.println("Get " + num + " from the queue");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

        producerThread.start();
        consumerThread.start();
    }
}

运行结果:


############

在 Java 的 main 方法中,代码语句是按照顺序执行的,除非有特定的并发控制结构或方法调用导致了程序的并发执行或阻塞。

当涉及到多线程的并发操作或阻塞调用时,程序的执行顺序可能会受到影响。让我们更详细地讨论一下这些情况:

  1. 多线程的并发操作

    • 如果在 main 方法中创建并启动了多个线程,那么这些线程可能会并发地执行。这意味着这些线程的执行顺序可能是不确定的,取决于操作系统和 JVM 的调度策略。
    • 如果这些线程共享了某些资源,比如共享内存或对象,而且没有正确的同步机制来保护这些资源,就可能导致竞态条件和线程安全问题。
  2. 阻塞调用

    • 阻塞调用会导致程序在执行到这些调用时暂停,并等待某些条件的发生。这些条件可能包括用户输入、文件读写、网络通信等。
    • 典型的阻塞调用包括 I/O 操作(比如文件读写、网络通信)、线程睡眠(Thread.sleep())、等待线程加入(Thread.join())等。
    • 当程序执行到阻塞调用时,当前线程会暂停执行,直到相应的条件满足或等待时间结束。这会影响到后续代码的执行顺序。

考虑以下示例,其中演示了多线程并发操作和阻塞调用:

在这个示例中,main 方法中创建了一个新线程,并且在主线程中执行了一个阻塞调用(Thread.sleep(2000))。这导致主线程在睡眠期间暂停执行,同时新线程继续执行。因此,输出的顺序可能是不确定的,取决于线程调度和睡眠时间的相对情况。

public class Main {
    public static void main(String[] args) {
        System.out.println("Start of main");

        // 创建并启动一个新线程
        Thread thread = new Thread(() -> {
            for (int i = 0; i < 5; i++) {
                System.out.println("Thread: " + i);
                try {
                    Thread.sleep(1000); // 线程睡眠1秒
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        thread.start();

        // 模拟阻塞调用:线程睡眠2秒
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("End of main");
    }
}

线程的start()方法只是进入了就绪队列,到分配到时间片就是运行状态了。
线程的几种情况:########################
1.正常执行完结束
2.没执行完,没时间片了,重新排队进入就绪队列
3.没执行完,自我阻塞,休眠(等待IO操作完成、等待锁资源、等待其他线程的通知,满足条件后进入就绪状态)

队列,堆,栈数据结构的异同:

队列、堆和栈是三种常见的数据结构,它们在存储和组织数据时有着不同的特点和用途。让我们来比较它们的异同:

  1. 队列(Queue)

    • 特点:队列是一种先进先出(FIFO)的数据结构,即先进入队列的元素会先被移除。
    • 操作:队列通常支持两种主要操作:入队(enqueue),将元素添加到队列的末尾;出队(dequeue),从队列的头部移除一个元素。
    • 应用:队列常用于需要按照顺序处理数据的场景,比如任务调度、消息传递等。
  2. 堆(Heap)

    • 特点:堆是一种特殊的树形数据结构,具有以下特点:父节点的值总是大于或等于(最大堆)或小于或等于(最小堆)子节点的值。
    • 操作:堆通常支持插入和删除操作,以及获取最大或最小值的操作。
    • 应用:堆常被用作优先队列的实现,用于高效地找到最大或最小值,以及在堆排序等算法中。
  3. 栈(Stack)

    • 特点:栈是一种后进先出(LIFO)的数据结构,即最后进入栈的元素会最先被移除。
    • 操作:栈通常支持压入(push)和弹出(pop)操作,分别用于在栈顶添加和移除元素。
    • 应用:栈常用于需要反向追溯的场景,比如函数调用、表达式求值、浏览器历史记录等。

异同点总结:

  • 共同点:队列、堆和栈都是常见的数据结构,用于存储和组织数据,并提供相应的操作。
  • 不同点:它们的特点和操作不同,适用于不同的场景。队列适用于先进先出的情况,堆适用于需要快速找到最大或最小值的情况,而栈适用于后进先出的情况。

总的来说,队列、堆和栈在数据存储和操作方面有着不同的优势和应用场景,选择合适的数据结构取决于具体的问题和需求。

栈和队列_停车场管理(栈和队列的应用)的空间复杂度-CSDN博客

缓冲区可以用什么数据结构?

缓冲区是一种用于临时存储数据的数据结构,常用于解决生产者-消费者问题或者在不同速度的系统之间进行数据交换。缓冲区的选择取决于具体的应用场景和需求,以下是一些常用的数据结构作为缓冲区的选择:

  1. 数组:数组是一种简单而高效的数据结构,可以作为缓冲区来存储一定量的数据。数组的主要优点是访问速度快,但其大小通常是固定的,不够灵活。

  2. 链表:链表是一种动态数据结构,可以灵活地添加和移除元素,因此也可以用作缓冲区。但链表的访问速度相对较慢,因为需要遍历整个链表来找到特定位置的元素。

  3. 队列:队列是一种先进先出(FIFO)的数据结构,非常适合用作缓冲区,特别是在生产者-消费者模型中。生产者可以将数据入队列,消费者则从队列中取出数据进行处理。

  4. 环形缓冲区(Circular Buffer):环形缓冲区是一种特殊的缓冲区,可以通过数组实现。它具有固定的大小,并且可以循环使用空间,避免了数据移动的开销。环形缓冲区常用于实时系统或者需要高效处理连续数据流的场景。

  5. 堆栈:堆栈是一种后进先出(LIFO)的数据结构,可以用作临时存储数据。但在大多数情况下,队列比堆栈更适合作为缓冲区,因为它们更符合生产者-消费者模型。

选择合适的缓冲区数据结构取决于需求,包括数据的访问模式、数据量的大小和性能要求等因素


当客户端发送按键事件,用户按下某个按钮,就将相应的值放入队列中,然后消费者线程将队列中的值取出并打印。

MyGUI:

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

public class MyGUI extends JFrame {
    private JButton button1, button2, button3;
    CustomQueue customQueue;
    public MyGUI() {
        // 设置窗口标题
        setTitle("GUI窗口");
        customQueue = new CustomQueue(5);
        // 创建按钮
        button1 = new JButton("按钮1");
        button2 = new JButton("按钮2");
        button3 = new JButton("按钮3");

        // 设置布局为流式布局
        setLayout(new FlowLayout());

        // 将按钮添加到窗口
        add(button1);
        add(button2);
        add(button3);

        // 添加按钮点击事件监听器
        button1.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                try {
                    customQueue.put(1);
                } catch (InterruptedException ex) {
                    ex.printStackTrace();
                }
            }
        });

        button2.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                try {
                    customQueue.put(2);
                } catch (InterruptedException ex) {
                    ex.printStackTrace();
                }
            }
        });

        button3.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                try {
                    customQueue.put(3);
                } catch (InterruptedException ex) {
                    ex.printStackTrace();
                }
            }
        });

        // 设置窗口大小和关闭操作
        setSize(300, 200);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setLocationRelativeTo(null); // 窗口居中显示
        setVisible(true); // 显示窗口
        // 消费者线程,从队列中获取数字
        Thread consumerThread = new Thread(() -> {
            try {
                while (true) {
                    int num = customQueue.get();
                    System.out.println("Get " + num + " from the queue");
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        consumerThread.setName("consumer");
        consumerThread.start();
    }

    public static void main(String[] args) {
        // 创建窗口对象,运行程序时,jvm会创建一个主线程来运行main方法。
        new MyGUI();//创建窗口的同时,创建了一个线程并且启动。
      Thread.currentThread().setName("MAIN");
    }
}


//主线程执行main方法,在创建窗口时又创建了一个消费者线程.
//无论android中还是pc客户端,主线程就是ui线程,子线程就是后台线程(用来执行耗时操作)!!!!!!!!!!!!!!!!!




//主线程执行main方法,在创建窗口时又创建了一个消费者线程.
//无论android中还是pc客户端,主线程就是ui线程,子线程就是后台线程(用来执行耗时操作)!!!!!!!!!!!!!!!!!

同步和异步是计算机编程中常用的两种处理方式:
同步(Synchronous):在同步操作中,任务按照顺序执行,每个任务都必须等待前一个任务完成后才能开始执行。在同步操作中,程序会阻塞(即暂停执行)直到操作完成。这意味着如果一个任务很耗时,整个程序可能会出现停滞或卡顿的情况。
异步(Asynchronous):在异步操作中,任务不需要等待前一个任务完成就可以开始执行。相反,程序会继续执行后续的任务,而不会被当前任务的执行阻塞异步操作通常会在后台线程或者通过回调函数来执行,这样可以提高程序的响应性和并发性。
在实际编程中,异步操作通常用于处理那些可能耗时的任务,比如网络请求、文件读写、数据库查询等。通过将这些任务设置为异步操作,可以避免阻塞主线程,保持程序的流畅性和响应性。
总的来说,同步操作是按顺序执行的,会阻塞程序的执行;而异步操作可以并发执行,不会阻塞程序的执行。

也就是说并发意味着异步,顺序意味着同步!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!


Android ANR:原理分析及解决办法 - 知乎 (zhihu.com)

android中的主线程,子线程:

activity
service
broadcastReceiver
当你启动一个新的Activity时,Android系统会在UI线程中执行相应的生命周期方法和界面更新操作。如果在Activity中执行了一些耗时操作,例如加载大量数据或进行网络请求,建议在子线程中执行,以避免阻塞主线程导致界面卡顿。
你可以在Activity中手动创建新的线程来执行耗时操作,或者使用异步任务(AsyncTask)、HandlerRxJava等方式来在后台线程中执行任务,然后在主线程中更新UI。这样可以确保在主线程中处理界面更新,同时避免阻塞主线程导致的ANR(Application Not Responding)错误。

启动一个服务(Service)不会在主线程中执行服务的代码逻辑。服务的生命周期方法(如onCreate()、onStartCommand()等)以及服务中的其他逻辑通常在主线程之外的后台线程中执行。这意味着服务的代码执行是在一个新的线程中进行的,而不会阻塞主线程。
默认情况下,服务会在主线程之外的工作线程中执行。这样可以确保服务可以在后台执行长时间运行的操作,而不会影响到应用程序的响应性能。然而,在服务中执行的代码仍然需要注意,尤其是避免执行耗时操作或阻塞线程。
如果你需要在服务中执行耗时操作,例如网络请求或大量数据处理,最好在服务内部创建新的线程或使用异步任务来执行这些操作,以免阻塞服务的工作线程。另外,你也可以考虑使用Android提供的后台处理机制,如IntentService或JobScheduler,来管理和执行后台任务,以便更好地管理资源和执行顺序。


广播接收器(BroadcastReceiver)的onReceive()方法默认在主线程(UI线程)中执行。这意呢确保广播接收器可以立即响应广播消息,但也意味着如果在onReceive()方法中执行耗时操作,会导致主线程阻塞,从而影响到应用的响应性能。
如果onReceive()方法中需要执行耗时操作,最好在其中启动一个新线程来执行,以免阻塞主线程。可以使用线程(Thread)、Handler、AsyncTask等方法来在后台线程中执行任务。或者,你也可以考虑使用IntentService或JobScheduler等机制来管理后台任务的执行,以便更好地控制资源的使用和执行顺序。
需要注意的是,由于onReceive()方法是在主线程中执行的,因此它执行的时间应该尽量短,以免影响到应用的性能和响应速度。

总结:主线程顺序执行,activity随便跳转,broadcastReceiver随便创建。在主线程中随便创建子线程/后台服务。一条原则:耗时的操作就放在子线程中去执行,采用这种异步的方式。

CustomQueue:

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;

public class CustomQueue {

    private BlockingQueue<Integer> queue;

    public CustomQueue(int capacity) {
        // 初始化一个容量为1的阻塞队列
        queue = new LinkedBlockingQueue<>(capacity);
    }

    public void put(int num) throws InterruptedException {
        // 将数字放入队列,如果队列已满,则循环等待直到成功放入
            try {
                queue.put(num);
                System.out.println("queue添加了"+num);
            } catch (InterruptedException e) {
                e.printStackTrace();
                // 重新尝试放入
            }

    }

    public int get() throws InterruptedException {
        // 从队列中获取数字,如果队列为空,则阻塞等待
        return queue.take();
    }


}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

给一个新项目配置conda环境的完整流程

创建环境&#xff0c;并指定python的版本&#xff0c;我这边指定为3.7&#xff1a; conda create --name [自定义的环境名] python3.7我这边假定我的环境名为grand&#xff1a; conda create --name grand python3.7创建成功后&#xff0c;初始化一下conda&#xff1a; source …

Google DeepMind: Many-Shot vs. Few-Shot

本文介绍了如何通过增大上下文窗口&#xff0c;利用大型语言模型&#xff08;LLMs&#xff09;进行多实例上下文学习&#xff08;Many-Shot In-Context Learning&#xff0c;ICL&#xff09;的方法。主要描述了现有的几实例上下文学习方法虽然在推理时能够通过少量例子学习&…

基于Java+SpringBoot+vue动物救助平台设计和实现

基于JavaSpringBootvue动物救助平台设计和实现 &#x1f345; 作者主页 央顺技术团队 &#x1f345; 欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd; &#x1f345; 文末获取源码联系方式 &#x1f4dd; &#x1f345; 查看下方微信号获取联系方式 承接各种定制系统 &#…

树莓派使用总结

手上拿到了一块Raspberry Pi 4B板子。研究一下怎么用。 安装系统 直接到官网【Raspberry Pi 】下载在线安装助手 安装好后&#xff0c;打开软件&#xff0c;选择好板子型号、系统、TF卡&#xff0c;一路下一步就行。 树莓派接口 直接查看官方的资料【Raspberry Pi hardwar…

基础算法之二分算法

前言 本次博客&#xff0c;将要介绍二分算法的基本原理以及如何使用&#xff0c;深入浅出 二分可以针对整型以及浮点型接下来对其讲解希望对小白有所帮助吧 整型的二分法 一般要在一个数组中猜出一个数是否存在我们可以遍历一遍整个数组&#xff0c;判断是否存在&#xff0…

Java面向对象编程

标题&#xff1a;Java面向对象编程 文章目录 标题&#xff1a;Java面向对象编程前言&#xff1a;面向对象的三条主线一、面向对象编程概述1.1 程序设计思路1.2 Java语言的基本元素&#xff1a;类和对象1.3 对象的内存解析 二、类的成员1—成员变量2.1 “变量”定义&分类2.2…

蓝桥杯备赛

关闭同步流&#xff1a; ios::sync_with_stdio(0);cin.tie(0);cout.tie(0); 注意数据范围&#xff1a;数据范围较大时干脆所有变量类型都定义成longlong等。 stl&#xff1a; sort函数 时间复杂度为nlog(n); sort(数组指针&#xff0c;从指针开始多少个数&#xff0c;great…

如何辨别:DNS污染or DNS劫持?

DNS劫持和DNS污染的情况在互联网中并不少见&#xff0c;到底是出现了DNS污染还是DNS劫持。什么是DNS污染&#xff1f;什么是DNS劫持&#xff1f;我们该如何辨别DNS污染和DNS劫持&#xff1f; DNS劫持&#xff1a; DNS 劫持是指恶意攻击者通过非法手段篡改了网络中的 DNS 服务…

HTML快速入门

HTML简介 HTML&#xff08;超文本标记语言&#xff09;是一种用于创建网页和Web应用程序的标记语言。它由一系列标签组成&#xff0c;每个标签通过尖括号来定义&#xff0c;并用于标记文本、图像、链接和其他内容。HTML标签描述了网页中的信息结构和布局&#xff0c;并定义了文…

[MySQL数据库] 索引与事务

1. 索引 1.1 概念 索引是一种特殊的文件&#xff0c;包含着对数据表里所有记录的引用指针.可以对表中的一列或多列创建索引,并指定索引的类型&#xff0c;各类索引有各自的数据结构实现. 1.2 作用 数据库中的表、数据、索引之间的关系&#xff0c;类似于书架上的图书、书籍…

【Redis】面试题汇总

Redis什么是Redis、使用场景有哪些Redis 为什么这么快&#xff1f;Redis 数据类型及使用场景五种常见的 Redis 数据类型是怎么实现&#xff1f;Redis是单线程吗Redis 采用单线程为什么还这么快&#xff1f;Redis 如何实现数据不丢失&#xff1f;Redis 如何实现服务高可用&#…

【复习笔记】FreeRTOS(六) 队列操作

本文是FreeRTOS复习笔记的第六节&#xff0c;队列操作。 上一篇文章&#xff1a; 【复习笔记】FreeRTOS(五)时间片调度 文章目录 1.队列操作1.1.队列操作过程1.2.队列操作常用的API函数 二、实验设计三、测试例程四、实验效果 1.队列操作 队列是为了任务与任务、任务与中断之间…

极狐GitLab x LigaAI,AI 时代研发提效新范式

GitLab 是一个全球知名的一体化 DevOps 平台&#xff0c;很多人都通过私有化部署 GitLab 来进行源代码托管。极狐GitLab 是 GitLab 在中国的发行版&#xff0c;专门为中国程序员服务。可以一键式部署极狐GitLab。 近日&#xff0c;极狐GitLab 和 LigaAI 宣布合作&#xff0c;双…

分布式锁设计

一、为什么需要分布式锁 1.1 单体项目同步实现 在单进程&#xff08;启动一个jvm&#xff09;的系统中&#xff0c;当存在多个线程可以同时改变某个变量&#xff08;可变共享变量&#xff09;时&#xff0c;就需要对变量或代码块做同步&#xff0c;使其在修改这种变量时能够线…

vue2中props属性设置一个对象或数组的默认值

在Vue.js中&#xff0c;如果您想要为一个props属性设置一个对象或数组的默认值&#xff0c;您应该使用一个函数来返回这个默认值。这是因为对象和数组是引用类型&#xff0c;直接将它们作为默认值可能会导致预设的默认值被所有实例共享&#xff0c;这不是我们想要的结果。 下面…

zabbix 自定义模板,邮件报警,代理服务器,自动发现与自动添加及snmp

目录 一. 自定义监控内容 1. 在客户端创建自定义 key 2. 在 web 页面创建自定义监控项模块 2.1 创建模板 2.2 创建应用集 2.3 创建监控项 2.4 创建触发器 2.5 创建图形 2.6 将主机与模板关联起来 登录测试 2.7 设置邮件报警 测试邮件报警 3. nginx 服务状况的检测…

Vue中SourceMap的使用方法详解

目录 一、概述 二、使用方法 三、生成SourceMap 四、优化 五、结语 一、概述 Vue.js是一套构建用户界面的渐进式框架&#xff0c;通过HTML模板或者直接写render函数可以快速开发单页应用。在开发过程中&#xff0c;很多时候我们需要调试代码&#xff0c;追踪错误。Vue官方…

Linux:调试器 - gdb

Linux&#xff1a;调试器 - gdb gbd基本概念gbd调试浏览断点运行变量 gbd基本概念 GDB (GNU Debugger) 是一个强大的命令行调试工具,用于调试各种编程语言(如C、C、Java、Python等)编写的程序。使用 gdb可以帮助开发人员更快地定位和修复程序中的缺陷,提高代码质量和开发效率。…

Python介绍(未完)

文章目录 Python 背景知识Python 是谁创造的&#xff1f;Python 可以用来干什么&#xff1f;Python 的优缺点 搭建 Python 环境安装 Python搭建 PyCharm 环境新工具到手&#xff0c;赶紧试试中文设置第一个Python程序 Python基础语法基础语法&#xff08;1&#xff09;常量和表…

Error : java 错误 : 不支持发行版本5 ( 完美解决)

解决方案 1. 原因 idea的默认配置JDK版本与当前项目所需版本不一样 方案一&#xff08;每一个项目可能都要配置一遍&#xff09; Ctrlshitalts 打开项目结构&#xff0c;设置项目所需的JDK版本&#xff0c;本项目需要JDK8 Modules的JDK版本为5&#xff0c;这时就会报Error …
最新文章