【JavaEE初阶】多线程6(线程池\定时器)

news2024/12/27 10:52:47

欢迎关注个人主页:逸狼


创造不易,可以点点赞吗~

如有错误,欢迎指出~



目录

 实例3:线程池

参数解释 

核心线程数, 最大线程数

允许空闲的最大时间 ,时间单位 

任务队列(阻塞队列) 

线程工厂=>工厂设计模式 

拒绝策略 

使用举例

模拟实现一个线程池(固定线程数目的线程池)

实例4:定时器

使用举例 

定时器的模拟实现

选定数据结构 

线程安全问题

不使用sleep 


 实例3:线程池

线程池 就是把线程提前从系统中申请好,放到一个地方,后面需要使用线程的时候,直接从这个地方来取,而不是从系统中重新申请,线程用完之后,也是会还回到刚才的地方.主要是解决随着业务上对于性能要求越来越高,线程创建开销的频次越来越多的 问题,

参数解释 

核心线程数, 最大线程数

允许空闲的最大时间 ,时间单位 

任务队列(阻塞队列) 

线程工厂=>工厂设计模式 

拒绝策略 

使用举例

ThreadPoolExecutor 是封装前的 ->定制性更强,用起来更麻烦

Executors 是封装过的->定制性比较弱,用起来简单 

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Demo30 {
    public static void main(String[] args) throws InterruptedException {
        ExecutorService service= Executors.newFixedThreadPool(4);//代表线程池中有4个线程
        for (int i = 0; i < 100; i++) {
            int id =i;//让id成为事实final,让lambda捕获
            service.submit(()->{
               Thread current = Thread.currentThread();//当前线程
                System.out.println("hello thread"+ id +","+ current.getName());
            });
        }

        //最好不要立即就终止, 可能任务还没执行完,线程就终止了
        Thread.sleep(2000);

        //把线程池里的线程都 终止掉
        service.shutdown();
        System.out.println("程序退出");
    }
}

加上 service.shutdown(),让前台线程强制结束

如何指定线程池中的线程个数 

模拟实现一个线程池(固定线程数目的线程池)

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;

class MyThreadPool{

    private BlockingQueue<Runnable> queue =new ArrayBlockingQueue<>(1000);
    //此处n表示创建几个线程
    public MyThreadPool(int n){
        //先创建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 void submit(Runnable runnable) throws InterruptedException {
        queue.put(runnable);
    }
}

public class Demo31_MyPool {
    public static void main(String[] args) throws InterruptedException {
        //测试代码
        MyThreadPool pool=new MyThreadPool(4);
        for (int i = 0; i < 1000; i++) {
            int id=i;
            pool.submit(()->{
                System.out.println("执行任务"+ id +"," + Thread.currentThread().getName());
            });//lambda对应的是"函数式接口", Runnable 也是同样符合这样的要求的

        }
    }
}

实例4:定时器

定时器相当于"闹钟",网络通信中,经常需要设定一个"超时时间",Java标准库中也提供了定时器实现

定时器在后端开发中特别重要和常用,和"阻塞队列" 类似,也会有专门的服务器(用来在分布式系统中 实现定时器的效果)

如果定的任务时间都是一样的值,接下来任务的执行顺序可能是串行的,也可能是并发的(取决于定时器的具体实现)

使用举例 

TimerTask本质上就是Runnable的进一步实现

import java.util.Timer;
import java.util.TimerTask;

public class Demo31 {
    public static void main(String[] args) {
        Timer timer=new Timer();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("hello3");
            }
        },3000);//表示延时3秒钟 打印hello3

        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("hello2");
            }
        },2000);

        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("hello1");
            }
        },1000);
        System.out.println("程序开始执行");
    }
}

定时器的模拟实现

定时器的实现步骤:

  1. 创建类,描述一个要执行的任务(任务的内容,任务的时间)
  2. 管理多个任务,通过一定的数据结构,把多个任务存起来
  3. 有专门的线程,执行这里面的任务
import java.util.ArrayList;
import java.util.List;
import java.util.PriorityQueue;
import java.util.concurrent.PriorityBlockingQueue;

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();
        }
    }
}

选定数据结构 

按照时间来执行任务,只要能够确定所有任务中时间最小的任务,判定其是否到达执行时间即可(其他时间的任务必定排在 时间最小任务后面),所以使用优先级队列是最好的选择

为啥不使用BlockingQueue阻塞队列作为实现定时器的数据结构?

  • 阻塞队列里的take里也有一把锁,容易出现死锁情况
  • 代码的复杂程度会增加

对于所有的修改操作都要加上锁

线程安全问题

通过对进队列和出队列进行加锁

使用wait操作,避免出现 "线程饿死" 这种情况

不使用sleep 

为啥使用wait,不使用sleep让线程阻塞?

业界实现定时器除了基于优先级队列的方式之外,还有一种典型的实现方式,"时间轮"(也是一种巧妙设计的数据结构)

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

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

相关文章

从零开始讲DDR(0)——DDR的前世今生

一、计算机组成 计算机组成结构&#xff08;Computer Architecture&#xff09;是计算机系统的核心&#xff0c;它定义了计算机的基本工作原理和设计模式。计算机的组成可以分成以下3大类&#xff1a;中央处理器&#xff08;CPU&#xff09;、存储器和输入/输出子系统。 1.1 中…

Linux文件IO-基础知识了解及文件描述符

1、简介 本章给大家介绍 Linux 应用编程中最基础的知识&#xff0c;即文件 I/O&#xff08;Input、Outout&#xff09;&#xff0c;文件 I/O 指的是对文件的输入/输出操作&#xff0c;说白了就是对文件的读写操作&#xff1b;Linux 下一切皆文件&#xff0c;文件作为 Linux 系…

深度学习 之 常见损失函数简介:名称、作用及用法

引言 在机器学习和深度学习中&#xff0c;损失函数&#xff08;Loss Function&#xff09;是模型训练过程中一个不可或缺的部分。它用来度量模型预测结果与真实值之间的差异&#xff0c;从而指导模型参数的优化。合理选择损失函数对于提高模型的准确性和泛化能力至关重要。本文…

Mint Expedition Season 3 拉开帷幕:登顶高峰的时刻到了

自 7 月 15 日 Mint Expedition 启动以来&#xff0c;Mint&#xff0c;一条专注于 NFT 行业的以太坊 Layer 2&#xff0c;日常交易量和交易额都出现了爆发式增长。这一成功离不开 Mint 社区的合作&#xff0c;包括 Minters、Web3 去中心化应用程序的开发者&#xff0c;以及大量…

模电模块(一)

这个看起来功能挺全的&#xff0c;就是小贵&#xff0c;有时间自己做一个&#xff1a; 首页-康威科技-淘宝网 (taobao.com) 画一个集成板&#xff0c;集合上述模块的功能。

深圳国际VR/AR博览会圆满落下帷幕

近日&#xff0c;深圳国际VR/AR博览会在深圳国际会展中心2号馆圆满落下帷幕。该展会于9月11日至13日举行&#xff0c;是一个与光博会同期举行的大型盛会。 据主办方介绍&#xff0c;深圳国际VR/AR博览会&#xff08;Shenzhen International VR/AR Expo&#xff09;&#xff0c;…

力扣最热一百题——缺失的第一个正数

目录 题目链接&#xff1a;41. 缺失的第一个正数 - 力扣&#xff08;LeetCode&#xff09; 题目描述 示例 提示&#xff1a; 解法一&#xff1a;标记数组法 1. 将非正数和超出范围的数替换 2. 使用数组下标标记存在的数字 3. 找到第一个未标记的位置 4. 为什么时间复杂…

【与C++的邂逅】--- C++的IO流

Welcome to 9ilks Code World (๑•́ ₃ •̀๑) 个人主页: 9ilk (๑•́ ₃ •̀๑) 文章专栏&#xff1a; 与C的邂逅 本篇博客我们来了解C中io流的相关知识。 &#x1f3e0; C语言输入输出 C语言中我们用到的最频繁的输入输出方式就是scanf ()与printf()。 sc…

数据处理与统计分析篇-day03-Numpy环境搭建

概述 python优势 Python作为当下最为流行的编程语言之一 可以独立完成数据分析的各种任务 数据分析领域里有海量开源库 机器学习/深度学习领域最热门的编程语言 在爬虫&#xff0c;Web开发等领域均有应用 常用开源库 numpy NumPy(NumericalPython) 是 Python 语言的一…

创客中国AIGC专题赛冠军天鹜科技:AI蛋白质设计引领者

“落霞与孤鹜齐飞,秋水共长天一色——这句出自《滕王阁序》的诗句,是我作为江西人熟记于心的佳句。它描绘的天地壮丽景色常浮现于我的脑海,正是这种豁达与壮观,启发我们将公司命名为‘天鹜科技’,我们希望将源自自然的蛋白质与现代科技的创新精神相结合,打造蛋白质设计与应用的…

OpenBayes 教程上新 | AI 时代的「神笔马良」,Hyper-SD 一键启动教程上线!

每次脑海中的画面栩栩如生&#xff0c;想画下来却难以下笔&#xff1f; 每次画完自己觉得非常像&#xff0c;但是旁人却一头雾水&#xff1f; 每次想用文生图&#xff0c;但不知道如何精确地输入 prompt&#xff1f; AI 时代的「神笔马良」Hyper-SD 来了&#xff01; 仅需简…

基本仪表放大器+基本电容耦合隔离放大器+OTA(基本OTA电路+OTA增益)

2024-9-18&#xff0c;星期三&#xff0c;21:37&#xff0c;天气&#xff1a;多云&#xff0c;心情&#xff1a;晴。大家中秋节都过的怎么样啊&#xff0c;如果没过爽也没有关系&#xff0c;因为再上八天班就能迎来10.1长假啦&#xff01;&#xff01;&#xff01;&#xff01;…

【机器学习】--- 自然语言推理(NLI)

引言 随着自然语言处理&#xff08;NLP&#xff09;的迅速发展&#xff0c;**自然语言推理&#xff08;Natural Language Inference, NLI&#xff09;**已成为一项重要的研究任务。它的目标是判断两个文本片段之间的逻辑关系。这一任务广泛应用于机器阅读理解、问答系统、对话…

五星级可视化页面(30):本系列最后一期,压轴出场。

不知不觉分享了30期高品质的五星级可视化大屏界面&#xff0c;该系列文章也该收尾了&#xff0c;本期为大家分享最后一批界面&#xff0c;我们下一个系列专辑见。

力扣之181.超过经理收入的员工

文章目录 1. 181.超过经理收入的员工1.1 题干1.2 准备数据1.3 题解1.4 结果截图 1. 181.超过经理收入的员工 1.1 题干 表&#xff1a;Employee -------------------- | Column Name | Type | -------------------- | id | int | | name | varchar | | salary | int | | mana…

W25QXX系列Flash存储器模块驱动代码

目录 W25QXX简介 硬件电路 W25Q128框图 Flash操作注意事项 驱动代码 W25QXX.h W25QXX.c W25QXX简介 W25Qxx系列是一种低成本、小型化、使用简单的非易失性存储器&#xff0c;常应用于数据存储、字库存储、固件程序存储等场景 存储介质&#xff1a;Nor Flash&#xff0…

Apache SeaTunnel Zeta引擎源码解析(三) Server端接收任务的执行流程

作者&#xff1a;刘乃杰 编辑整理&#xff1a;曾辉 引入 本系列文章是基于 Apache SeaTunnel 2.3.6版本&#xff0c;围绕Zeta引擎给大家介绍其任务是如何从提交到运行的全流程&#xff0c;希望通过这篇文档&#xff0c;对刚刚上手SeaTunnel的朋友提供一些帮助。 我们整体的文…

ios xib 子控件约束置灰不能添加约束

添加约束时发现置灰不可点的问题 layout切换为inferred&#xff0c;就可以添加约束了

[SIGGRAPH-24] CharacterGen

[pdf | code | proj] LRM能否用于3D数字人重建&#xff1f;问题在于&#xff1a;1&#xff09;缺少3D数字人数据&#xff1b;2&#xff09;重建任意姿态的3D数字人不利于后续绑定和驱动。构建3D数字人数据集&#xff1a;在VRoidHub上采集数据&#xff0c;得到13746个风格化角色…

青柠视频云——记一次大华摄像头公网语音对讲失败的问题分析

今天有客户反馈&#xff0c;使用大华摄像头接入青柠视频云&#xff0c;在公网环境下无法进行语音对讲&#xff0c;用户的设备是支持语音对讲的。 这是用户提供的注册截图&#xff0c;看起来也没什么问题&#xff0c;而且用户摄像头带有拾音功能和外放喇叭。 于是我们联系客户开…