线程池,定时器以及阻塞队列(生产者/消费者模型)

💓 博客主页:从零开始的-CodeNinja之路

⏩ 收录专栏:线程池,定时器以及阻塞队列(生产者/消费者模型)

🎉欢迎大家点赞👍评论📝收藏⭐文章
在这里插入图片描述

实现线程池,定时器以及阻塞队列,生产者/消费者模型

  • 线程池
      • 线程池是什么
      • Executors创建线程池的四种方式
      • 线程池底层ThreadPoolExecutor的六大参数
      • 拒绝策略
      • 实现线程池
  • 定时器
      • 定时器的定义
      • 标准库中的定时器
      • 定时器的实现
  • 阻塞队列
      • 阻塞队列的概念
      • 生产者消费者模型
      • 阻塞队列实现(生产者/消费者模型)

在这里插入图片描述

在这里插入图片描述

线程池

线程池是什么

虽然创建线程/销毁线程的开销
线程池相当于是一个大池子,池子里放着默认的10个已创建的空闲的线程,每当使用时直接从池子里取出一个空闲的线程进行使用,使用完以后在放回池子里面,这样就减少了每次创建线程和销毁线程的资源浪费.

Executors创建线程池的四种方式

  • newFixedThreadPool:创建固定线程数的线程池
  • newCachedThreadPool:创建线程数目动态增长的线程池.
  • newSingleThreadExecutor:创建只包含单个线程的线程池.
  • newScheduledThreadPool:设定延迟时间后执行命令,或者定期执行命令.是进阶版的Timer.

线程池底层ThreadPoolExecutor的六大参数

Executors本质上是ThreadPoolExecutor类的封装.

ThreadPoolExecutor提供了更多的可选参数,可以进⼀步细化线程池行为的设定.

  • corePoolSize:正式员工的数量.正式员工,⼀旦录用,永不辞退)

  • maximumPoolSize:正式员工+临时工的数目.(临时工:⼀段时间不干活,就被辞退).

  • keepAliveTime:临时工允许的空闲时间.

  • unit:keepaliveTime的时间单位,是秒,分钟,还是其他值.

  • workQueue:传递任务的阻塞队列

  • threadFactory:创建线程的工厂,参与具体的创建线程工作.通过不同线程工厂创建出的线程相当于

对⼀些属性进行了不同的初始化设置.

拒绝策略

RejectedExecutionHandler:拒绝策略,如果任务量超出公司的负荷了接下来怎么处理.

  • AbortPolicy():超过负荷,直接抛出异常.
  • CallerRunsPolicy():调用者负责处理多出来的任务.
  • DiscardOldestPolicy():丢弃队列中最老的任务.
  • DiscardPolicy():丢弃新来的任务.

实现线程池

核心操作为submit,将任务加入线程池中

  1. 使⽤Worker类描述一个工作线程.使用Runnable描述⼀个任务.

  2. 使用一个BlockingQueue组织所有的任务

  3. worker线程要做的事情:不停的从BlockingQueue中取任务并执行.

  4. 指定⼀下线程池中的最大线程数maxWorkerCount;当当前线程数超过这个最大值时,就不再新增 线程了

.

class MyThreadPool {
    private BlockingQueue<Runnable> queue = new LinkedBlockingQueue<>();
    // 通过这个⽅法, 来把任务添加到线程池中.
    public void submit(Runnable runnable) throws InterruptedException {
        queue.put(runnable);
    }
    // n 表⽰线程池⾥有⼏个线程.
// 创建了⼀个固定数量的线程池.
    public MyThreadPool(int n) {
        for (int i = 0; i < n; i++) {
            Thread t = new Thread(() -> {
                while (true) {
                    try {
                            // 取出任务, 并执⾏~~
                        Runnable runnable = queue.take();
                        runnable.run();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
            t.start();
        }
    }
}
// 线程池
public class Demo {
    public static void main(String[] args) throws InterruptedException {
        MyThreadPool pool = new MyThreadPool(4);
        for (int i = 0; i < 1000; i++) {
            pool.submit(new Runnable() {
                @Override
                public void run() {
// 要执⾏的⼯作
                    System.out.println(Thread.currentThread().getName() + " hell
                }
            });
        }
    }
}

定时器

定时器的定义

定时器也是软件开发中的⼀个重要组件.类似于⼀个"闹钟".达到⼀个设定的时间之后,就执行某个指定好的代码.
定时器是⼀种实际开发中非常常用的组件.
比如网络通信中,如果对方500ms内没有返回数据,则断开连接尝试重连.
比如⼀个Map,希望里面的某个key在3s之后过期(自动删除).

标准库中的定时器

  • 标准库中提供了⼀个Timer类.Timer类的核心方法为 schedule .
  • schedule 包含两个参数.第⼀个参数指定即将要执行的任务代码,第二个参数指定多长时间之后执行(单位为毫秒).
 Timer timer = new Timer();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("hello");
            }
        }, 3000);

定时器的实现

定时器的构成

  • ⼀个带优先级队列(不要使用PriorityBlockingQueue,容易死锁!)
  • 队列中的每个元素是⼀个Task对象.
  • Task中带有⼀个时间属性,队首元素就是即将要执行的任务
  • 同时有⼀个worker线程⼀直扫描队首元素,看队首元素是否需要执行
  1. Timer类提供的核心接口为schedule,用于注册⼀个任务,并指定这个任务多长时间后执行.
public class MyTimer {
        public void schedule(Runnable command, long after) {
        }
    }
  1. Task类用于描述⼀个任务(作为Timer的内部类).里面包含⼀个Runnable对象和⼀个time(毫秒时间戳)
    这个对象需要放到优先队列中.因此需要实现 Comparable 接口.
  class MyTask implements Comparable<MyTask> {
                public Runnable runnable;
                // 为了⽅便后续判定, 使⽤绝对的时间戳.
                public long time;
                public MyTask(Runnable runnable, long delay) {
                    this.runnable = runnable;
                   // 取当前时刻的时间戳 + delay, 作为该任务实际执⾏的时间戳
                    this.time = System.currentTimeMillis() + delay;
                }
                @Override
                public int compareTo(MyTask o) {
// 这样的写法意味着每次取出的是时间最⼩的元素.
// 到底是谁减谁?? 俺也记不住!!! 随便写⼀个, 执⾏下, 看看效果~~
                    return (int)(this.time - o.time);
                }
             }
  1. Timer实例中,通过PriorityQueue来组织若干个Task对象.通过schedule来往队列中插⼀入个个Task对象.
class MyTimer {
    // 核⼼结构
    private PriorityQueue<MyTask> queue = new PriorityQueue<>();
    // 创建⼀个锁对象
    private Object locker = new Object();
    public void schedule(Runnable command, long after) {
// 根据参数, 构造 MyTask, 插⼊队列即可.
        synchronized (locker) {
            MyTask myTask = new MyTask(runnable, delay);
            queue.offer(myTask);
            locker.notify();
        }
    }
}
  1. Timer类中存在⼀个worker线程,⼀直不停的扫描队首 元素,看看是否能执行这个任务.所谓"能执行"指的是该任务设定的时间已经到达了.
 // 在这⾥构造线程, 负责执⾏具体任务了.
    public MyTimer() {
        Thread t = new Thread(() -> {
            while (true) {
                try {
                   synchronized (locker) {
// 阻塞队列, 只有阻塞的⼊队列和阻塞的出队列, 没有阻塞的查看队⾸元素.
                        while (queue.isEmpty()) {
                            locker.wait();
                        }
                        MyTask myTask = queue.peek();
                        long curTime = System.currentTimeMillis();
                        if (curTime >= myTask.time) {
// 时间到了, 可以执⾏任务了
                            queue.poll();
                            myTask.runnable.run();
                        } else {
// 时间还没到
                            locker.wait(myTask.time - curTime);
                        }
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        t.start();
    }

阻塞队列

阻塞队列的概念

阻塞队列是⼀种特殊的队列.也遵守"先进先出"的原则.
阻塞队列能是⼀种线程安全的数据结构,并且具有以下特性:

  • 当队列满的时候,继续⼊队列就会阻塞,直到有其他线程从队列中取走元素.
  • 当队列空的时候,继续出队列也会阻塞,直到有其他线程往队列中插入元素.

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

生产者消费者模型

生产者消费者模式就是通过⼀个容器来解决⽣产者和消费者的强耦合问题。
生产者和消费者彼此之间不直接通讯,而通过阻塞队列来进行通讯,所以生产者生产完数据之后不用等待消费者处理,直接扔给阻塞队列,消费者不找生产者要数据,而是直接从阻塞队列里取.

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

例如在"秒杀"场景下,服务器同⼀时刻可能会收到⼤量的⽀付请求.如果直接处理这些⽀付请求, 服务
   器可能扛不住(每个⽀付请求的处理都需要⽐较复杂的流程).这个时候就可以把这些请求都放到⼀个
   阻塞队列中,然后再由消费者线程慢慢的来处理每个⽀付请求. 这样做可以有效进行"削峰",防止服务器被突然到来的⼀波请求直接冲垮.

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

⽐如过年⼀家⼈⼀起包饺⼦.⼀般都是有明确分⼯,⽐如⼀个⼈负责擀饺⼦⽪,其他⼈负责包.擀饺⼦
⽪的⼈就是"⽣产者",包饺⼦的⼈就是"消费者".
擀饺⼦⽪的⼈不关⼼包饺⼦的⼈是谁(能包就⾏,⽆论是⼿⼯包,借助⼯具,还是机器包),包饺⼦的⼈也
不关⼼擀饺⼦⽪的⼈是谁(有饺⼦⽪就⾏,⽆论是⽤擀⾯杖擀的,还是拿罐头瓶擀,还是直接从超市买
的).

阻塞队列实现(生产者/消费者模型)

  • • 通过"循环队列"的方式来实现.
  • • 使用synchronized进行加锁控制.
  • • put插入元素的时候,判定如果队列满了,就进行wait.(注意,要在循环中进行wait被唤醒时不⼀定
    队列就不满了,因为同时可能是唤醒了多个线程).
  • • take取出元素的时候,判定如果队列为空,就进行wait.(也是循环wait)
import java.util.Random;

public class BlockingQueue {
    private int[] items = new int[1000];
    private volatile int size = 0;
    private volatile int head = 0;
    private volatile int tail = 0;
    public void put(int value) throws InterruptedException {
        synchronized (this) {
// 此处最好使⽤ while.
// 否则 notifyAll 的时候, 该线程从 wait 中被唤醒,
// 但是紧接着并未抢占到锁. 当锁被抢占的时候, 可能⼜已经队列满了
// 就只能继续等待
            while (size == items.length) {
                wait();
            }
            items[tail] = value;
            tail = (tail + 1) % items.length;
            size++;
           notifyAll();
        }
    }
    public int take() throws InterruptedException {
        int ret = 0;
        synchronized (this) {
            while (size == 0) {
                wait();
            }
            ret = items[head];
            head = (head + 1) % items.length;
            size--;
            notifyAll();
        }
        return ret;
    }
    public synchronized int size() {
        return size;
    }
    // 测试代码
    public static void main(String[] args) throws InterruptedException {
        BlockingQueue blockingQueue = new BlockingQueue();
        Thread customer = new Thread(() -> {
            while (true) {
                try {
                    int value = blockingQueue.take();
                    System.out.println(value);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "消费者");
        customer.start();
        Thread producer = new Thread(() -> {
            Random random = new Random();
            while (true) {
                try {
                    blockingQueue.put(random.nextInt(10000));
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
           }, "⽣产者");
        producer.start();
        customer.join();
        producer.join();
    }
}

在这里插入图片描述
如果觉得文章不错,期待你的一键三连哦,你个鼓励是我创作的动力之源,让我们一起加油,顶峰相见!!!💓 💓 💓

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

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

相关文章

STM32 UART/USART在无线通信模块和蓝牙设备中的应用案例

STM32微控制器与无线通信模块和蓝牙设备的结合&#xff0c;为物联网和无线通信应用提供了广泛的可能性。下面是两个典型的应用案例&#xff0c;展示了STM32的UART/USART与无线通信模块&#xff08;如Wi-Fi模块&#xff09;和蓝牙设备的集成。 ✅作者简介&#xff1a;热爱科研的…

以小猪o2o生活通v17.1为例简要分析SWOOLE加密破解,swoole_loader加密破解swoole加密逆向后的代码修复流程(个人见解高手掠过)

现在用Php加密五花八门除了组件就是混淆&#xff0c;在组件里面响当当的还属swoole&#xff0c;SWOOLEC是不错的国产加密&#xff0c;值得推荐官方宣称是永远无法破解的加密算法&#xff0c;针对swoole compiler的代码修复我谈谈我的看法&#xff0c;以小猪o2o生活通&#xff0…

C#入门详解_01_课程简介、C#语言简介、开发环境和学习资料的准备

文章目录 1. 课程简介2. C#语言简介3.开发环境与学习资料 1. 课程简介 开设本课程的目的 传播C#开发的知识&#xff0c;让更多的人有机会接触到软件开发行业引导有兴趣或者想转行的朋友进入软件开发行业 课程内容 完整讲述C#语言在实际软件开发中的应用采用知识讲述加实例程序…

springboot149智慧图书管理系统设计与实现

智慧图书管理系统的设计与实现 摘 要 如今社会上各行各业&#xff0c;都在用属于自己专用的软件来进行工作&#xff0c;互联网发展到这个时候&#xff0c;人们已经发现离不开了互联网。互联网的发展&#xff0c;离不开一些新的技术&#xff0c;而新技术的产生往往是为了解决现…

python 时间计算器

第一个函数time_calculator可以根据用户的需求增加或减少时间。 第二个函数calculate_time_difference可以计算两个时间点之间的差异。这两个函数都是自动化测试和时间管理中非常有用的工具。 from datetime import datetime, timedeltadef time_calculator(start_date, days…

Python爬虫获取淘宝商品详情页数据|实现自动化采集商品信息

近年来&#xff0c;随着互联网的发展&#xff0c;越来越多的数据以网页的形式存在于各个网站上。对于数据分析师、研究员或者仅仅是对数据感兴趣的人来说&#xff0c;如何高效地提取和分析网页数据成为了一项重要的技能。Python作为一门强大的编程语言&#xff0c;通过其丰富的…

JSR303参数校验-SpringMVC

文章目录 JSR303技术标准简介JSR303标准几个具体实现框架validation-apijakarta.validation-apihibernate-validatorspring-boot-starter-validation Spring Validationjavax.validation.constraints包下提供的注解org.hibernate.validator.constraints包扩展的注解校验注解默认…

GPT在地学、GIS、气象、农业、生态、环境等领域中的高级应用

详情点击公众号&#xff1a;技术科研吧 链接&#xff1a;GPT在地学、GIS、气象、农业、生态、环境等领域中的高级应用 一开启大模型 1 开启大模型 1)大模型的发展历程与最新功能 2)大模型的强大功能与应用场景 3)国内外经典大模型&#xff08;ChatGPT、LLaMA、Gemini、DAL…

[Vulnhub靶机] DriftingBlues: 3

[Vulnhub靶机] DriftingBlues: 3靶机渗透思路及方法&#xff08;个人分享&#xff09; 靶机下载地址&#xff1a; https://download.vulnhub.com/driftingblues/driftingblues3.ova 靶机地址&#xff1a;192.168.67.19 攻击机地址&#xff1a;192.168.67.3 一、信息收集 1.…

leetcode206反转链表|详细算法讲解学习

题目 https://leetcode.cn/problems/reverse-linked-list/ 这道题对于刚开始学习数据结构和算法的人来说有点难&#xff0c;是入门的重要典型题目&#xff1b;但等数据结构入门之后&#xff0c;这就会是一道非常简单的题目了。 算法一&#xff08;算法正确但超出时间限制&am…

Linux|Grep 命令的 12 个实用示例

您是否曾经遇到过在文件中查找特定字符串或模式的任务&#xff0c;但不知道从哪里开始查找&#xff1f;那么&#xff0c;grep 命令可以拯救你&#xff01; grep 是一个功能强大的文件模式搜索器&#xff0c;每个 Linux 发行版都配备了它。如果出于某种原因&#xff0c;它没有安…

Django的web框架Django Rest_Framework精讲(一)

文章目录 Django Rest_Framework1. DRF介绍2.DRF特点3.环境安装与配置&#xff08;1&#xff09;DRF需要以下依赖&#xff08;2&#xff09;创建django项目 4.序列化器的使用&#xff08;1&#xff09;创建序列化器 5. 反序列化器使用 Django Rest_Framework 1. DRF介绍 Djan…

选择排序、冒泡排序----C语言数据结构

目录 引言 1.选择排序的实现1.1选择排序的时间复杂度2.冒泡排序的实现2.1冒泡排序的时间复杂度分析及优缺 引言 选择排序&#xff08;Selection Sort&#xff09;是一种简单直观的排序算法&#xff0c;它的基本思想是每次从未排序的元素中选择最小&#xff08;或最大&#xff…

VitePress-08-文档中代码组的使用

什么是代码组 代码组 : 就是代码块的集合。一个代码组中可以包含多个代码块。 效果 &#xff1a; 用页签的形式将不同的代码块分开展示。 代码组的语法格式 代码组的语法格式较为固定&#xff0c;如下 &#xff1a; ::: code-group代码块1的类型 [代码块1展示的页签名称]代码块…

全新 鸿蒙系统

一&#xff0c; 开发框架 基础 二&#xff0c; 官网地址 文档开发&#xff1a;华为HarmonyOS智能终端操作系统官网 | 应用设备分布式开发者生态 三&#xff0c;基础了解 鸿蒙系统是基于 js 和 ts 衍生出来的一个东西 要学 arkts 就要学习 js 和 ts 语法 四&#xff0c…

Avalonia学习(二十二)-数据库操作端

开始项目式的例子&#xff0c;但是不方便给大家贴代码了。 内容很多&#xff0c;只能演示一个界面&#xff0c;例子上传。 我不擅长界面美化和配色&#xff0c;有兴趣的可以继续完善&#xff0c;当前实现mysql。 最近所有样例的地址&#xff1a; GitHub - jinyuttt/Avalonia…

基于条纹投影的三维形貌与形变测量技术研究

▒▒本文目录▒▒ 一、 引言二、基于条纹投影轮廓术的形变测量实验2.1 实验光路2.2 实验结果 三、参考文献四、结论五、软硬件系统开发六、交流与合作 一、 引言 作为一种典型的三维形貌重建方法&#xff0c;条纹投影轮廓测量术&#xff08;Fringe Projection Profilometry&am…

java入门、环境配置及其特点介绍

目录 一、java语言的重要特点 二、java开发工具包&#xff08;JDK&#xff09;及其环境配置 三、java入门代码 四、Java运行机制 五、java学习方法 一、java语言的重要特点 java是面向对象的Java是健壮性的。Java具有强类型机制、异常处理、垃圾的自动收集等特点java语言是跨…

炼丹师必备平台,云上训练无压力—GpuMall智算云

说回作为一个“炼丹师”在选择炼丹平台的核心需求&#xff1a;【数据传输速率快慢】or【机器租用价格性价比】or【可租用机器类型多少】or【镜像版本选择多样性】or【机器稳定性】等等 是不是以上任何一个条件&#xff0c;都可能会影响你对平台的选择呢&#xff1f;纵观市面上…

python coding with ChatGPT 打卡第16天| 二叉树:完全二叉树、平衡二叉树、二叉树的所有路径、左叶子之和

相关推荐 python coding with ChatGPT 打卡第12天| 二叉树&#xff1a;理论基础 python coding with ChatGPT 打卡第13天| 二叉树的深度优先遍历 python coding with ChatGPT 打卡第14天| 二叉树的广度优先遍历 python coding with ChatGPT 打卡第15天| 二叉树&#xff1a;翻转…