【JavaEE】多线程代码案例(2)

在这里插入图片描述

🎏🎏🎏个人主页🎏🎏🎏
🎏🎏🎏JavaEE专栏🎏🎏🎏
🎏🎏🎏上一篇文章:多线程代码案例(1)🎏🎏🎏

文章目录

  • 1.线程池
    • 1.1概念
    • 1.2线程池如何提高效率
    • 1.3标准库线程池的参数分析
    • 1.4模拟实现一个线程池
      • 1.4.1一个固定数目的线程池
      • 1.4.2含有最大线程数的线程池
    • 1.5线程池优点
    • 2.定时器
    • 2.1概念
    • 2.2Java标准库中的定时器(Timer)
    • 2.3模拟实现定时器
      • 2.3.1定时器的需求
      • 2.3.2实现需求的技术
      • 2.3.3代码的实现
    • 1.4总结

1.线程池

1.1概念

将你需要用到的线程提前创建好,然后放到用户态通过数据结构的形式来管理。

1.2线程池如何提高效率

我们直接创建线程是内核态与用户态两一起配合完成的,如果频繁的去创建线程销毁线程,这样效率就会大大降低并且内核在完成一些任务是不可控的,面对这种情况我们就可以提前将我们需要用到的线程创建好放在用户态种通过数据结构管理起来,当需要使用的时候就通过用户态来调用,不要用就放回用户态中,减少内核态的参与相当于减少了不可控性那么效率就会提高。

1.3标准库线程池的参数分析

ThreadPoolExecutor
在这里插入图片描述

  1. int corePoolSize int maximumPoolSize
    corepoolSize(核心线程数)——根据CPU的逻辑核心数决定的
    maximumPoolSize(最大线程数)——由核心线程数+非核心线程数
    对线程池中的线程分为两种:核心线程和非核心线程
    核心线程:当线程池被创建了,核心线程也就有了。
    非核心线程:当任务过多的时候,核心线程处理不过来的时候,线程池就会临时创建线程来分担任务,当任务变轻的时候,那么这些非核心线程就会被回收,这样当任务繁重的时候,增加一些非核心线程就会提高效率,当任务空闲的时候就可以减少开销。
    那么最大的线程数应该设多少比较合适呢?
    关于设多少比较合适不仅仅和电脑的配置有关系还和代码类型有关系。
    代码类型在理想的情况下分为两种:CPU密集类型和IO密集类型
    CPU密集类型:
    代码中基本都是算术运算,条件判断,循环判断,函数调用这些都是需要大量调用CPU来参加工作的,那么这种最大的线程数应该要小于等于逻辑核心数。
    IO密集类型:
    IO类型的每一个线程消耗的CPU只有一点点,影响的主要是其他方面比如网卡带宽,硬盘访问…,那么最大线程数可以大于等于逻辑核心数。
    但我们生活中可不会有这种理想情况发生,一般都是CPU密集型于IO密集型结合的情况,那么这个最大的线程数怎么去确定呢,可以根据做实验的方式(控制变量法)来确定一个相对正确的数据。
  2. long keepAliveTime TimeUnit unit
    long keepAliveTime——允许非核心线程最大的空闲时间
    TimeUnit unit——空闲时间单位
    给定时间可以增加容错率,防止任务少的时候突然任务剧增,这样就可以给回收非核心线程缓冲时间。
  3. BlockingQueue workQueue
    BlockingQueue workQueue——线程池的任务队列
    线程池会提供submit方法,让其他线程将任务提交给线程池,此时的线程池就需要队列这样的数据结构,将任务管理起来,这个队列存储的元素其实就是Runnable对象,要执行的逻辑其实就是run方法中的内容
  4. ThreadFactory threadFactory
    ThreadFactory threadFactory——java标准库中提供的工厂类
    当我们需要创建一个点,有两种方式一种是笛卡尔坐标表示法,另一种是极坐标表示法
class Point {
    point(double x,double y) {
    
    }
    point(double r,double a) {

    }
}

此时的两种方式都是通过构造方法来创建的,但是这两种方法构成不了重载,所以无法实现,因为在某些特殊情况构造方法会带来一些麻烦,就出现了工厂方法来封装一些这些构造方法,这种模式叫工厂模式

class Point {
    public static point makePointByXY(double x, double y) {
        Point p = new Point();
        p.set(x);
        p.set(y);
        return p;
    }
    public static point makePointByRA(double r, double a) {
        Point p = new Point();
        p.set(r);
        p.set(a);
        return p;
    }
}

上述代码就是一种通过工厂方法来封装这些构造点的方法。
5. RejectedExecutionHandler handler
RejectedExecutionHandler handler——拒绝策略,是一种以枚举的方式表示的。

  1. AbortPolicy():超出负荷,直接抛出异常
  2. CallerRunsPolicy():调用者负责处理多出来的任务
  3. DiscardOldestPolicy():丢弃队列中最老的任务
  4. DiscardPolicy():丢弃新来的任务
    由于ThreadPoolExecutor用起来非常费劲,于是就提供了几个工厂类例如:Executor
    通过工厂类中提供的方法可以创建线程池的几种方式:
  5. Executors.newFixedThreadPool()——创建固定线程数目的线程池
  6. Executors.newSingleThreadExecutor()——创建一个只包含单个线程的线程池
  7. Executors.newScheduledThreadPool(4)——创建一个固定线程个数, 但是任务延时执行的线程池
    创建一个固定线程数的线程池:
public static void main(String[] args) {
    ExecutorService service = Executors.newFixedThreadPool(10);
    for (int i = 0; i <=50000 ; i++) {
        int id = i;
        service.submit(new Runnable() {
            @Override
            public void run() {
                System.out.println("hello " + id + ", " + Thread.currentThread().getName());
            }
        });
    }
}

1.4模拟实现一个线程池

  1. 需要若干个线程
  2. 需要任务队列
  3. 需要submit方法

1.4.1一个固定数目的线程池

//创建一个简单的线程
public class MyThreadPollBasicEdition {
    //创建一个任务队列
    public BlockingQueue<Runnable> queue = new ArrayBlockingQueue<>(1000);
    //初始化线程池
    public MyThreadPollBasicEdition(int n) {
        for (int i = 0; i < n; i++) {
            Thread t = new Thread(()-> {
                    try {
                        while(true) {
                            Runnable runnable = queue.take();
                            runnable.run();
                        }
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
            });
            t.start();
        }
    }
    //提供submit方法
    public void submit(Runnable runnable) throws InterruptedException {
        queue.put(runnable);
    }

    public static void main(String[] args) throws InterruptedException {
        MyThreadPollBasicEdition myThreadPollBasicEdition = new MyThreadPollBasicEdition(10);
        for (int i = 0; i < 50000; i++) {
            int id = i;
            myThreadPollBasicEdition.submit(new Runnable() {
                @Override
                public void run() {
                    System.out.println("hello " + id + ", " + Thread.currentThread().getName());
                }
            });
        }
    }
}

1.4.2含有最大线程数的线程池

ic class MyThreadPollAdvancedEdition {
    //创建一个任务队列的对象
    public BlockingQueue<Runnable> queue = new ArrayBlockingQueue(1000);
    //创建一个最大线程数
    public  int maxThreadSize = 0;
    //创建一个集合来存储若干个线程
    public List<Thread> threadList = new ArrayList<>();

    //创建构造方法
    public MyThreadPollAdvancedEdition(int corePoolSize, int maxThreadSize) {
       this.maxThreadSize = maxThreadSize;
        for (int i = 0; i < corePoolSize; i++) {
            Thread t = new Thread(()->{
                    try {
                        while (true) {
                            Runnable runnable = queue.take();
                            runnable.run();
                        }
                    }
                    catch (InterruptedException e) {
                        e.printStackTrace();
                    }
            });
            t.start();
            threadList.add(t);
        }
    }
    public void submit(Runnable runnable) throws InterruptedException {
        queue.put(runnable);
        //如果队列中的任务过多,导致线程不够用,可以增加一些线程
        if(queue.size() >=200 && threadList.size() < maxThreadSize) {
           Thread thread = new Thread(()-> {
               try {
                   while(true) {
                       Runnable runnable1 = queue.take();
                       runnable1.run();
                   }
               }
               catch (InterruptedException e) {
                   e.printStackTrace();
               }
           });
           thread.start();
           threadList.add(thread);
        }
    }

    public static void main(String[] args) throws InterruptedException {
        MyThreadPollAdvancedEdition myThreadPollAdvancedEdition = new MyThreadPollAdvancedEdition(10,20);
        for (int i = 0; i < 50000; i++) {
            int id = i;
            myThreadPollAdvancedEdition.submit(new Runnable() {
                @Override
                public void run() {
                    System.out.println("hello " + id + ", " + Thread.currentThread().getName());
                }
            });
        }
    }
}

1.5线程池优点

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

2.定时器

2.1概念

用于实现定时操作、周期性任务和超时控制的作用

2.2Java标准库中的定时器(Timer)

Timer提供了一个schedule方法

public static void main(String[] args) {
    Timer timer = new Timer();
    timer.schedule(new TimerTask() {
        @Override
        public void run() {
            System.out.println("hello,2000");
        }
    },2000);
    timer.schedule(new TimerTask() {
        @Override
        public void run() {
            System.out.println("hello,1000");
        }
    },1000);
    timer.schedule(new TimerTask() {
        @Override
        public void run() {
            System.out.println("hello,3000");
        }
    },3000);
}

2.3模拟实现定时器

2.3.1定时器的需求

  1. 能够延时执行任务和定时执行任务
  2. 能够管理多个任务

2.3.2实现需求的技术

  1. 需要一个描述任务和指定时间的类(本质就是一个Runnable)
  2. 需要一个数据结构来管理多个任务(优先级队列)
  3. 需要一个线程来扫描数据结构管理的任务

2.3.3代码的实现

//1.定义一个TimeTask类表示一个任务,这个类中需要任务执行的时间和描述任务。
class TimeTask implements Comparable<TimeTask> {
    public Runnable runnable;
    //此时的time不是程序等待的时间,而是一个绝对时间
    public long time;
    public TimeTask(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(TimeTask o) {
        return (int) (this.time - o.time);
    }
}
//2.定义一个数据结构来管理多个任务,此处我们优先级队列来管理多个任务,因为用其他的数据结构去管理的话,
// 就需要不断去扫描数据结构中满足要求的成员,遇到数据量庞大的那么开销就巨大,得不偿失,
// 用优先级队列可以避免这种情况,由于要求等待时间短的先运行,那么我们可以定义一个小根堆。
class MyTime{
    Object locker = new Object();
    public PriorityQueue<TimeTask> queue = new PriorityQueue<>();
    //初始化线程,并且调用任务
    public MyTime() {
        //定义一个扫描线程来获取堆顶任务
        Thread t = new Thread(()-> {
            try {
                while(true) {
                    synchronized (locker) {
                        if (queue.size() == 0) {
                            locker.wait();
                        }
                    //取小堆中堆顶的任务
                    TimeTask task = queue.peek();
                    //获取当前的时间戳
                    long curTime = System.currentTimeMillis();
                    //看任务的时间是否到了
                    if(curTime >= task.getTime()) {
                        //时间到了,则执行任务
                        task.run();
                        //任务执行完之后,则将队列中的堆顶任务消除
                        queue.poll();
                    } else {
                        //任务时间没到,则堵塞,阻塞多久?堵塞任务等待的时间
                            locker.wait(task.getTime() - curTime);
                        }
                    }
                }
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        });
        t.start();
    }
    //提供一个schedule方法来创建任务
    public void schedule(Runnable runnable,long delay) {
        synchronized (locker) {
            TimeTask timeTask = new TimeTask(runnable,delay);
            queue.offer(timeTask);
            //唤醒调用任务的线程
            locker.notify();
        }
    }
}
public class MyTimerTest {
    public static void main(String[] args) {
        MyTime myTime = new MyTime();
        myTime.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("hello,1000");
            }
        },1000);

        myTime.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("hello,2000");
            }
        },2000);
        myTime.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("hello,3000");
            }
        },3000);
    }
}

1.4总结

  1. 创建一个类,表示一个任务(Runnable 任务本体 time任务的执行时间)
  2. 引入数据结构来管理多个任务(用的是优先级队列,省去遍历的开销)
  3. 引入扫描线程,不停的循环获取队列队首任务,判定是否到时间,到时间就执行,并且出队列没到时间就阻塞。
  4. 引入锁,针对队列出和入的操作
  5. 解决忙等问题,引入wait和notify,队列为空wait(死等)队首任务没到时间wait(带有超时时间)这里不要用sleep(sleep通过interrupt唤醒是非常规手段,sleep不会释放锁,会影响后续插入任务)
  6. 引入比较规则,让TimeTask可以按照时间先后来制定优先级。

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

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

相关文章

花键参数确定的流程是怎么样的?

继续花键的话题&#xff0c;今天跟小伙伴们一同学习一下&#xff1a;渐开线花键的参数确定的一般流程及基本方法。 前面有好几篇介绍了花键的基本参数的概念&#xff0c;包括规格、模数、齿数、压力角等等。以及花键的定心方式&#xff0c;内外花键的配合方式。那么这些参数的…

基于docker轻松部署selenium grid环境

做web自动化的同学都知道selenium grid非常好用&#xff0c;但是环境配置特别麻烦&#xff0c;很多人都躺在了环境搭建。那么有没有更简单的方式呢&#xff0c;答案是肯定的&#xff0c;今天我们就用docker来完成它&#xff0c;希望对大家有帮助。 一、环境准备 准备一台 Linu…

6个步骤实现Postman接口压力测试(建议收藏)

&#x1f345; 视频学习&#xff1a;文末有免费的配套视频可观看 &#x1f345; 点击文末小卡片 &#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 这里讲是postman做接口并发测试&#xff0c;基础用法不做赘述 1、第一步接口可以通的情况下点击…

Web应用防火墙用在哪些场景?

WAF是Web Application Firewall的缩写&#xff0c;翻译为“Web应用防火墙”是一种网络安全设备或服务&#xff0c;用于保护Web应用程序免受各种网络攻击和漏洞的影响。 WAF特别设计用于识别和阻止特定于Web应用程序的攻击&#xff0c;例如SQL注入、跨站脚本(XSS)、跨站请求伪造…

2024最新中级会计职称考试全科题库资料。

1.根据消费税法律制度的规定&#xff0c;下列各项中&#xff0c;属于消费税征税范围的是&#xff08;&#xff09;。 A.汽车轮胎 B.食用酒精 C.铂金首饰 D.体育上用的发令纸 答案&#xff1a;C 解析&#xff1a;选项ABD均不属于消费税的征税范围。 2.甲企业&#xff08;…

PDF内存如何变小,PDF内存压缩,PDF内存变小怎么调整

在数字化时代&#xff0c;pdf已成为工作、学习和生活中不可或缺的文件格式。它以其跨平台兼容性和安全性受到广大用户的喜爱。然而&#xff0c;随着pdf文件中嵌入的图片、图形和文本内容的增多&#xff0c;文件大小往往会变得相当可观&#xff0c;给文件的传输和存储带来一定的…

2024亚太杯中文赛B题全保姆教程

B题 洪水灾害的数据分析与预测 问题 1. 请分析附件 train.csv 中的数据&#xff0c;分析并可视化上述 20 个指标中&#xff0c;哪 些指标与洪水的发生有着密切的关联&#xff1f;哪些指标与洪水发生的相关性不大&#xff1f;并 分析可能的原因&#xff0c;然后针对洪水的提前预…

Jenkins 下使用 Node 和 Npm(借助 nvm-wrapper 插件)构建前端程序

一、前言 搭建完Jenkins后&#xff0c;如何使用node进行构建前端呢&#xff0c;多个项目会使用的node的多个版本。如何动态指定node的版本进行构建呢。 方案一&#xff1a; 安装多个node版本&#xff0c;然后进行指定。这样比较麻烦。 方案二&#xff1a; 使用Jenkins的nv…

JavaSE (Java基础):面向对象(下)

8.7 多态 什么是多态&#xff1f; 即同一方法可以根据发送对象的不同而采用多种不同的方式。 一个对象的实际类型是确定的&#xff0c;但可以指向对象的引用的类型有很多。在句话我是这样理解的&#xff1a; 在实例中使用方法都是根据他最开始将类实例化最左边的类型来定的&…

基于docker环境及Harbor部署{很简短一点了,耐心看吧}

用到的环境&#xff1a; docker 、nacos、compose、harbor&#xff08;自行安装 ,以下连接作为参考&#xff09; nacos&#xff1a;史上最全整合nacos单机模式整合哈哈哈哈哈_nacos 源码启动 单机模式-CSDN博客 docker、compose、harbor:史上最全的整合Harbor安装教程&#…

Qt实现检测软件是否多开

Qt实现检测软件是否多开 在桌面软件开发中&#xff0c;软件通常要设置只允许存在一个进程&#xff0c;像一些熟知的音乐软件&#xff0c;QQ音乐这种。而这些软件在限制只有一个进程的同时&#xff0c;通常还会有双击桌面图标唤醒已运行的后台进程的功能。关于双击桌面唤醒已运…

斯坦福提出首个开源视觉语言动作大模型OpenVLA

OpenVLA&#xff1a;开源视觉语言动作大模型 摘要模型结构训练数据训练设施实验总结和局限性 项目主页 代码链接 论文链接 模型链接 摘要 现有的VLA(Vision-Language-Action )模型具有这些局限性&#xff1a; 1)大多封闭且开放&#xff1b; 2)未能探索高效地为新任务微调VLA的方…

vue3 【提效】使用 CSS 框架 UnoCSS 实用教程

该换种更高效的方式写 CSS 啦&#xff0c;举个例&#xff1a; <div class"flex"> </div>相当于 <div class"flex"> </div> <style> .flex {display: flex; } </style>当然&#xff0c;还有超多强大的功能帮我们提升…

【后端面试题】【中间件】【NoSQL】MongoDB查询过程、ESR规则、覆盖索引的优化

任何中间件的面试说到底都是以高可用、高性能和高并发为主&#xff0c;而高性能和高并发基本是同时存在的。 性能优化一直被看作一个高级面试点&#xff0c;因为只有对原理了解得很透彻的人&#xff0c;在实践中才能找准性能优化的关键点&#xff0c;从而通过各种优化手段解决性…

为什么人员定位系统很有必要性?

人员定位系统在现代社会和企业环境中具有极高的必要性&#xff0c;这主要体现在以下几个方面&#xff1a; 一、安全保障 二、提升效率 三、管理优化 四、增强合规性 综上所述&#xff0c;人员定位系统通过提供实时、准确的位置信息&#xff0c;为企业带来了安全保障、效率提升…

香橙派AIpro实测:YOLOv8便捷检测,算法速度与运行速度结合

香橙派AIpro实测&#xff1a;YOLOv8便捷检测&#xff0c;算法速度与运行速度结合 文章目录 香橙派AIpro实测&#xff1a;YOLOv8便捷检测&#xff0c;算法速度与运行速度结合一、引言二、香橙派AIpro简介三、YOLOv8检测效果3.1 目标检测算法介绍3.1.1 YOLO家族3.1.2 YOLOv8算法理…

500mA、低压差、低噪声、超快、无需旁路电容的CMOS LDO稳压器RT9013

一般描述 RT9013 SOT23-5封装的外观和丝印 RT9013 是一款高性能的 500mA LDO 稳压器&#xff0c;具有极高的 PSRR 和超低压差。非常适合具有苛刻性能和空间要求的便携式射频和无线应用。 RT9013的静态电流低至25μA&#xff0c;进一步延长了电池的使用寿命。RT9013 也适用于低…

Element中的日期时间选择器DateTimePicker和级联选择器Cascader

简述&#xff1a;在Element UI框架中&#xff0c;Cascader&#xff08;级联选择器&#xff09;和DateTimePicker&#xff08;日期时间选择器&#xff09;是两个非常实用且常用的组件&#xff0c;它们分别用于日期选择和多层级选择&#xff0c;提供了丰富的交互体验和便捷的数据…

私域和社群的差别是什么?

社群就是拉很多人建群就可以了&#xff0c;但是私域不是&#xff0c;这里有三点不同 1、私域的用户来源&#xff0c;不仅仅是微信&#xff0c;而是基于一定的联系形成的链接&#xff0c;比如买了商家的货&#xff0c;反复购买觉得好&#xff0c;推荐给亲朋好友的二次开发用户&…

nanodiffusion代码逐行理解之Attention

目录 一、注意力中的QKV二、注意力中的位置嵌入三、注意力中的多头四、注意力和自注意力五、注意力中的encode和decoder 一、注意力中的QKV 简单来说&#xff1a; Q: 要查询的信息 K: 一个索引&#xff0c;要查询的向量 V: 我们查询得到的值 复杂一点的解释&#xff1a; Query…