【JavaEE】线程池和定时器

news2024/9/23 15:28:28

  🔥个人主页: 中草药

🔥专栏:【Java】登神长阶 史诗般的Java成神之路


✏️一.线程池

        在Java中,线程池(Thread Pool)是一种用于管理并发线程的机制,它提供了一种创建、复用和管理一组线程的方法,这些线程可以用来执行提交的任务。线程池的使用可以显著提高程序的性能和响应能力,尤其是在处理大量并发任务时。Java的java.util.concurrent包提供了丰富的API来创建和管理线程池,其中最核心的接口是ExecutorService,以及其实现类ThreadPoolExecutor。 

创建

Java中线程池的创建主要通过Executors工厂类或直接使用ThreadPoolExecutor类来完成:

  1. 使用Executors工厂类

    • newSingleThreadExecutor():创建一个单线程的线程池,它只会创建一个线程来执行任务。
    • newFixedThreadPool(int nThreads):创建一个固定大小的线程池,线程数量由nThreads参数确定。
    • newCachedThreadPool():创建一个可缓存的线程池,线程数量没有上限,当长时间没有新任务时,空闲线程会被终止。
    • newScheduledThreadPool(int corePoolSize):创建一个可以安排任务的线程池,可以指定延迟执行任务或定期执行任务。
  2. 使用ThreadPoolExecutor

  • ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue):这是一个构造函数,可以自定义线程池的核心参数,如核心线程数、最大线程数、线程空闲时间、工作队列等。

使用

  1. 提交任务:使用execute(Runnable command)submit(Callable<T> task)方法提交任务到线程池。
  2. 关闭线程池:使用shutdown()方法关闭线程池,等待所有已提交的任务完成;使用shutdownNow()方法立即关闭线程池并尝试取消正在执行的任务。

举例

  • 使用Executors.newFixedThreadPool (5) 能创建出固定包含10个线程的线程池
  • 返回值类型为ExecutorService
  • 通过ExecutorService.submit可以注册一个任务到线程池中
public static void main(String[] args) throws InterruptedException {
        ExecutorService service= Executors.newFixedThreadPool(5);
        for (int i = 0; i < 100; i++) {
            int id=i;
            service.submit(()->{
                System.out.println("执行任务"+id+";"+Thread.currentThread().getName());
            });
        }
        // 最好不要立即就终止, 可能使任务还没执行完呢, 线程就被终止了.
        Thread.sleep(2000);

        // 关闭线程池
        service.shutdown();
        System.out.println("程序退出");
    }

线程池配置参数分析*

ThreadPoolExecutor 提供了更多的可选参数, 可以进⼀步细化线程池⾏为的设定
  1. 核心线程数-CorePoolSize

    • 参数名:corePoolSize
    • 描述:线程池中保持的线程的最少数量。即使线程池中没有任务需要执行,只要线程的数量不超过corePoolSize,线程池就不会销毁线程。这样可以保证线程池随时准备接收新的任务,而不需要频繁地创建和销毁线程。
  2. 最大线程数-MaximumPoolSize

    • 参数名:maximumPoolSize
    • 描述:线程池中允许的最大线程数量。当线程池中的活动线程数量达到maximumPoolSize后,线程池不会创建新的线程,而是将任务放入工作队列中等待执行。如果工作队列已满,线程池会采取拒绝策略处理新任务。
  3. 空闲线程存活时间-KeepAliveTime

    • 参数名:keepAliveTime
    • 描述:当线程池中的线程数量超过corePoolSize时,超出的线程会在没有任务可执行时等待的时间长度。如果在keepAliveTime时间内没有新任务到来,超出的线程将被终止。这有助于控制线程池的规模,避免在任务量减少时线程资源的浪费。
  4. 时间单位-Unit

    • 参数名:unit
    • 描述:配合keepAliveTime使用,指定空闲线程存活时间的单位,如毫秒、秒、分钟等。
  5. 工作队列-WorkQueue

    • 参数名:workQueue
    • 描述:用于存放等待执行的任务的阻塞队列。当线程池中的线程数量达到corePoolSize且所有线程都在执行任务时,后续到达的任务会被放置在工作队列中等待执行。常见的队列类型有ArrayBlockingQueue(有界队列)、LinkedBlockingQueue(有界或无界队列)、SynchronousQueue(不存储元素的队列)等。
  6. 拒绝策略-RejectedExecutionHandler

    • 参数名:handler
    • 描述:当线程池无法接受新任务时(即线程数量达到maximumPoolSize且工作队列已满)所采取的策略。

常见的拒绝策略

  • AbortPolicy:抛出RejectedExecutionException异常。
  • CallerRunsPolicy:由调用者线程执行该任务,即直接在调用execute方法的线程中执行任务。
  • DiscardPolicy:静默丢弃无法处理的任务。
  • DiscardOldestPolicy:丢弃队列中最老的任务,并尝试再次提交新任务。

模拟实现

  • 核心操作为submit,将任务加入线程池
  • 使用Runnable描述一个任务
  • 使用一个BlockingQueue组织所有任务
  • 不停从BlockingQueue中取任务并执行
class MyThreadPool{
    private BlockingQueue<Runnable> queue=new ArrayBlockingQueue<>( 1000);

    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) {
                        throw new RuntimeException(e);
                    }
                }
            });
            t.start();
        }
    }

    /**
     * 添加任务
     * @param runnable
     */
    public void submit(Runnable runnable){
        try{
            queue.put(runnable);
        }catch (InterruptedException e){
            throw new RuntimeException(e);
        }
    }
}

测试代码

public static void main(String[] args) {
        MyThreadPool pool=new MyThreadPool(5);
        for (int i = 0; i < 100; i++) {
            int id=i;
            pool.submit(()->{
                System.out.println("执行任务"+id+";"+Thread.currentThread().getName());
            });
        }
    }

测试结果

✒️二.定时器

        在Java中,定时器(Timer)是一个重要的工具,用于执行定时任务,无论是单次的还是周期性的,类似于一个闹钟,到达一个设定的时间之后就会执行某个指定好的代码Java提供了多种方式来实现定时任务。

基本使用

其中最常用的是java.util.Timer类和java.util.concurrent.ScheduledExecutorService接口。下面将详细介绍这两种实现方式。

java.util.Timer 类

java.util.Timer 是一个简单的定时器类,它使用一个单独的守护线程来调度和执行任务。它通常与java.util.TimerTask一起使用,后者是java.util.Timer类的任务执行单元。

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

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

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

注意事项

  • 在使用定时器时,一定要处理好任务执行中可能出现的异常,否则守护线程可能因为异常而终止,导致定时器失效。
  • 确保在应用程序结束前正确地关闭定时器,避免线程泄露和资源浪费。
  • 考虑到线程安全和并发控制,ScheduledExecutorService在大多数情况下是更好的选择,尤其是当任务需要并发执行时。

模拟实现

package demo;

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

/**
 * 模拟实现定时器
 */
class MyTimerTask implements Comparable<MyTimerTask> {
    private Runnable runnable;
    private long time;
    //此处的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);
    }
}

class MyTimer{
    private PriorityQueue<MyTimerTask> queue=new PriorityQueue<>();
    //如果用List不是最好的选择,在后续执行列表中任务时,需要依次遍历每个元素
    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();
                       if (System.currentTimeMillis()>=current.getTime()){
                           //执行该任务
                           current.run();
                           //将执行过的任务从队列中删除
                           queue.poll();
                       }else{
                           //暂且不执行
                           locker.wait(current.getTime()-System.currentTimeMillis());
                       }
                   }
               }
           }catch (InterruptedException e){
               throw new RuntimeException(e);
           }
        });
        t.start();
    }

    public void schedule(Runnable runnable,long delay){
        synchronized(locker){
            MyTimerTask current=new MyTimerTask(runnable,delay);
            queue.offer(current);
            locker.notify();
        }
    }


}

测试代码

public static void main(String[] args) {
        MyTimer timer=new MyTimer();

        timer.schedule(()->{
            System.out.println("倒计时 2");
        },2000);

        timer.schedule(()->{
            System.out.println("倒计时 1");
        },3000);

        timer.schedule(()->{
            System.out.println("倒计时 3");
        },1000);
        //证明优先级队列的作用
    }

 结果

🖋️三.总结与反思 

        在深入研究Java并发编程的过程中,线程池和定时器是两个不可或缺的概念。它们不仅体现了Java在处理并发和定时任务上的强大功能,也反映了现代软件工程中资源管理和效率优化的重要性。以下是我在学习这两个主题后的总结与反思。

线程池:高效资源管理的艺术

        线程池,顾名思义,是一个预先创建好的线程集合,用于执行提交的任务。它通过复用现有线程,避免了线程创建和销毁的开销,从而提高了系统的响应速度和资源利用率。线程池的配置参数,如核心线程数、最大线程数、工作队列类型等,需要根据具体的应用场景和系统资源进行细致调整。合理配置的线程池能够显著提升并发任务的处理能力,减少资源浪费,是高性能服务器应用的基石。

        在实际应用中,我深刻体会到线程池的配置需要平衡并发度与系统负载。过多的线程可能导致系统资源紧张,而过少的线程则可能无法充分利用系统资源。此外,线程池的管理,如任务的排队策略、异常处理、线程的生命周期管理等,也是实现高效、健壮系统的关键。

定时器:精准时间控制的利器

定时器则是在Java中执行定时任务的重要工具。无论是简单的java.util.Timer,还是更强大的java.util.concurrent.ScheduledExecutorService,它们都提供了执行周期性或一次性定时任务的能力。定时器的使用极大地丰富了Java在事件驱动、任务调度等方面的功能,是实现自动化运维、数据定时处理等场景的基础。

在使用定时器时,我注意到几个关键点:首先,选择合适的定时器类型至关重要。Timer简单易用,但ScheduledExecutorService提供了更高级的调度策略和更好的并发支持。其次,定时任务的异常处理不容忽视,否则可能导致任务执行失败而不被察觉。最后,正确关闭定时器,避免线程泄露,是编写健壮程序的必要步骤。

反思

        通过学习和实践线程池与定时器,我深刻理解到并发编程的复杂性和魅力。在设计系统时,不仅要考虑功能的实现,还要兼顾性能、资源管理和错误处理。线程池和定时器的合理应用,能够显著提升软件的响应速度和稳定性,但同时也对程序员提出了更高的要求。

        面向未来,随着云计算和分布式系统的普及,线程池和定时器的概念将更加重要。如何在分布式环境中高效、安全地管理线程和执行定时任务,将成为新一代软件工程师面临的新挑战。掌握线程池和定时器的原理及最佳实践,无疑将为迎接这些挑战打下坚实的基础。

        总之,Java中的线程池和定时器不仅是编程技巧的体现,更是现代软件工程思想的反映。通过深入学习和不断实践,我期待能够将这些知识应用于更复杂的系统设计和开发中,创造出既高效又可靠的软件产品。

🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀

以上,就是本期的全部内容啦,若有错误疏忽希望各位大佬及时指出💐

  制作不易,希望能对各位提供微小的帮助,可否留下你免费的赞呢🌸

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

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

相关文章

【C++】一文掌握C++的四种类型转换 --- static_cast、reinterpret_cast、const_cast、dynamic_cast

当面对两个选择时&#xff0c;抛硬币总能奏效。 并不是因为它总能给出对的答案&#xff0c; 而是在你把它抛在空中的那一秒里。 你突然就知道&#xff0c;你希望的结果是什么了。 --- 曾小贤 《爱情公寓》--- 一文掌握C的四种类型转换 1 C中的类型2 类型转换3 四种类型转换…

一次caffeine引起的CPU飙升问题

背景 背景是上游服务接入了博主团队提供的sdk&#xff0c;已经长达3年&#xff0c;运行稳定无异常&#xff0c;随着最近冲业绩&#xff0c;流量越来越大&#xff0c;直至某一天&#xff0c;其中一个接入方&#xff08;流量很大&#xff09;告知CPU在慢慢上升且没有回落的迹象&…

Godot《躲避小兵》实战之创建玩家场景

项目设置完之后&#xff0c;我们就可以开始处理玩家控制的角色。 这里我们将玩家放在一个单独的场景当中&#xff0c;这样做的好处是在游戏的其他部分做出来之前&#xff0c;我们就可以对其进行单独测试。 节点结构 场景是一个节点树结构&#xff0c;因此一个场景需要有一个…

设计模式六大原则之:依赖倒置原则

1. 依赖倒置原则简介 依赖倒置原则(Dependency Inversion Principle, DIP) 是面向对象设计的核心原则之一&#xff0c;由罗伯特马丁(Robert C. Martin)提出&#xff0c;旨在降低类间的依赖度&#xff0c;使之更易于维护和扩展。该原则主张高层模块不应该依赖于底层模块&#x…

江科大/江协科技 STM32学习笔记P23

文章目录 DMA直接存储器存取DMA简介存储器映像DMA框图DMA基本结构存储器到存储器的数据转运ADC扫描模式和DMA配合使用流程 DMA直接存储器存取 DMA简介 DMA进行存储器到存储器的数据转运&#xff0c;比如Flash里的一批数据转运到SRAM里&#xff0c;需要软件触发&#xff0c;使用…

JQuery实现的时间插件源码附注释

HTML页面代码 <!DOCTYPE HTML> <html> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <meta content="width=device-width, initial-scale=1, maximum-scale=1,user-scalable=no;" name="…

【方案】SRM系统整体设计方案(解决方案+实现源码)

一、项目理解 二、总体解决方案概述 三、业务解决方案详述 四、端到端的采购流程管理 1. 采购计划 2. 采购寻源(招投标、询报价、竞价) 3. 合同管理 4. 订单执行与供应商协同 5. 采购分析与评估 五、支撑流程 1. 物资主数据管理 2. 供应商管理 3. 目录管理 4. 评标专家库管理 5…

grom接入Prometheus,grafana

在同级目录下分别创建 docker-compose.yml&#xff0c;与prometheus.yml 配置文件 version: 3.8services:prometheus:image: prom/prometheuscontainer_name: prometheusports:- "9090:9090" # Prometheus Web UI 端口volumes:- ./prometheus.yml:/etc/prometheus…

opencv-python实战项目八:根据颜色抠出图片中感兴趣区域

文章目录 一&#xff0c;简介二&#xff0c;实现方案三、算法实现步骤3.2 处理颜色蒙版&#xff1a;3.3 取出图片中蒙版对应区域 四&#xff0c;整体代码五&#xff0c;效果&#xff1a; 一&#xff0c;简介 本项目旨在开发一个基于OpenCV的图像处理工具&#xff0c;实现根据颜…

商贸城小程序系统开发制作方案

商贸城作为集批发、零售、展示、交流于一体的综合性商业体。通过商贸城小程序系统促进商家与消费者之间的互动&#xff0c;实现线上线下流量的无缝对接。一、用户需求分析 1、顾客需求&#xff1a; 快速查找店铺信息&#xff1b; 在线浏览商品和服务&#xff1b; 实现线上预约、…

【网站项目】SpringBoot675学生心理压力咨询评判

&#x1f64a;作者简介&#xff1a;拥有多年开发工作经验&#xff0c;分享技术代码帮助学生学习&#xff0c;独立完成自己的项目或者毕业设计。 代码可以私聊博主获取。&#x1f339;赠送计算机毕业设计600个选题excel文件&#xff0c;帮助大学选题。赠送开题报告模板&#xff…

Vue启动时报异常 ‘error:03000086:digital envelope routines::initialization error‘

问题描述 启动Vue项目时&#xff0c;突发报如下异常&#xff1a; opensslErrorStack: [error:03000086:digital envelope routines::initialization error,error:0308010C:digital envelope routines::unsupported],library: digital envelope routines,reason: unsupported,…

CentOS7下载与安装 即配置网卡

CentOS 7是什么? CentOS7是基于RHEL的企业级Linux操作系统&#xff0c;引入了Systemd、XFS文件系统和Docker支持。它提供了新的软件包、工具和性能调优选项&#xff0c;同时加强了系统安全和稳定性。总的来说&#xff0c;CentOS7是一个稳定、安全、长期支持的操作系统&#xf…

【wiki知识库】09.欢迎页面展示(浏览量统计)SpringBoot部分

&#x1f34a; 编程有易不绕弯&#xff0c;成长之路不孤单&#xff01; 大家好&#xff0c;我是熊哈哈&#xff0c;这个项目从我接手到现在有了两个多月的时间了吧&#xff0c;其实本来我在七月初就做完的吧&#xff0c;但是六月份的时候生病了&#xff0c;在家里休息了一个月的…

【和可被 K 整除的子数组】python刷题记录

R4-前缀和专题 class Solution:def subarraysDivByK(self, nums: List[int], k: int) -> int:ret0# 存储当前位置的上一个位置的前缀和的余数加上当前位置的值对K的余数pre_mod0dictdefaultdict(int)dict[0]1for i in range(len(nums)):pre_mod(pre_modnums[i])%k# 如果能在…

Kubernetes 基础概念介绍

1. 应用部署方式 传统部署&#xff1a;互联网早期&#xff0c;会直接将应用程序部署在物理机上 优点&#xff1a;简单&#xff0c;不需要其它技术的参与 缺点&#xff1a;不能为应用程序定义资源使用边界&#xff0c;很难合理地分配计算资源&#xff0c;而且程序之间容易产生影…

投票小程序App功能开发源码技术实现

随着移动互联网的快速发展&#xff0c;小程序作为一种轻量级应用&#xff0c;因其无需安装、即用即走的特点&#xff0c;在各类应用场景中迅速普及。投票小程序作为其中的一种&#xff0c;因其便捷性和实用性&#xff0c;广泛应用于各类活动、问卷调查及意见收集中。本文将围绕…

Linux-Shell入门-05

1.Shell的概念 1.1 什么是shell Shell脚本语言是实现Linux/UNIX系统管理及自W动化运维所必备的重要工具&#xff0c; Linux/UNIX系统的底层及基础应用软件的核心大都涉及Shell脚本的内容。Shell是一种编程语言, 它像其它编程语言如: C, Java, Python等一样也有变量/函数/运算符…

上海“创投十九条”明确政府引导基金带动作用,银行如何挖掘投贷联动增长潜力?

发展创业投资是促进科技、产业、金融良性循环的重要举措。为促进创业投资高质量发展&#xff0c;近几个月来&#xff0c;“国创投十七条”“上海创投十九条”等政策陆续发布&#xff0c;明确指出发挥政府引导基金带动作用&#xff0c;进一步加大对战略性新兴产业和未来产业的支…

空指针异常(NullPointerException)以及解决方案

之前我们提到过&#xff0c;在学习数组这一篇章时&#xff0c;有两种运行时异常&#xff0c;可能会反复的出现,首先第一个就是我们之前所讲述的&#xff0c;数组下标越界异常&#xff08;ArrayIndexOutOfBoundsException&#xff09;&#xff0c;如需查看详情&#xff0c;可跳转…