Java多线程案例——定时器

news2025/2/28 6:35:23

一,定时器

1.定时器的概念

定时器是Java开发中一个重要的组件(功能类似于闹钟),可以指定一个任务在多长时间后执行(尤其在网络编程的时候,如果网络卡顿很长时间没有响应用户的需求,此时可以使用定时器来终止用户的请求),所以一个定时器最少具有两个功能:

  1. 一个需要执行的任务

  1. 指定的时间去执行任务

在Java标准中提供了Timer类来封装定时器这样的功能。

2.Timer类

Timer类是Java标准库提供的内置类,其核心方法是schedule方法;

Timer类构造方法

Timer()

创建一个新的计时器

Time(boolean isDaemon)

创建一个新的定时器,其相关线程可以指定为 run as a deamon

Time(String name)

创建一个新的定时器,其相关线程具有指定的名称

Time(String name, boolean isDaemon)

创建一个新的定时器,其相关线程具有指定的名称,可以指定为 run as a deamo

Timer类的构造方法一共有4种,一般最常用的就是第一种无参的构造方法

schedule方法

schedule方法是Timer类的核心,定时器所执行的操作都是由该方法提供,所以我们来看一下schedule方法的源码:

  1. 该方法是一个普通方法,在使用前需要先创建Timer类对象

  1. 该方法有两个参数:第一个是TimerTask task(重写内部的run方法来指定定时器的任务),第二个是long delay(描述的是多长时间后执行该任务,单位是ms)

  1. 其中这里的TimerTask类似于之前创建的线程中的Runnable,都是指定一个具体的任务

定时器任务示例

/**
 * 创建一个定时器任务
 * 任务内容:输出hello world
 * 时间:1000ms之后执行
 */
import java.util.Timer;
import java.util.TimerTask;

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

执行结果:

程序将会在1000ms毫秒之后打印“hello word”,但是此时进程并没有结束,因为定时器创建出来的线程默认属于前台线程,前台线程会阻止进程的结束。

二,模拟实现简单的定时器

定时器需要满足两个功能:

1.让被注册的任务能够在指定的时间被执行

2.一个定时器可以注册N个任务,N个任务按照最初约定的时间,按顺序执行

解决办法:

  1. 对于第一个功能,我们只需要单独在定时器内部搞个线程,让这个线程周期性的扫描,判定任务是否到时间了,如果到时间了就执行,否则就不执行

  1. 对于第二个问题,我们需要一个数据结构来保存这些任务,因为任务需要按照时间的先后顺序来执行,所以这里采用优先级队列来保存任务(同时这个优先级队列需要按照指定的优先级来保存),又因为在多线程的环境下使用定时器,所以优先级队列需要带有阻塞功能,即采用PriorityBlockingQueue这样的数据结构。

代码如下:

package ThreadLearning;

import java.util.concurrent.PriorityBlockingQueue;

/**
 * 使用一个类来表示一个定时器中的任务
 */
class MyTask implements Comparable<MyTask> {
    //要执行的任务
    private Runnable runnable;

    //任务啥时候执行(使用毫秒时间戳表示)
    private long time;

    public MyTask(Runnable runnable, long time) {
        this.runnable = runnable;
        this.time = time;
    }

    //获取当前任务的时间
    public long getTime() {
        return time;
    }

    //执行时间
    public void run() {
        runnable.run();
    }

    //重写compareTo方法来指定优先级队列的优先级
    @Override
    public int compareTo(MyTask o) {
        return (int) (this.time - o.time);
    }
}

class MyTimer {
    //扫描线程
    private Thread t = null;

    //阻塞队列用来保存任务
    //传入的参数是MyTask这个类
    private PriorityBlockingQueue<MyTask> queue = new PriorityBlockingQueue<>();

    public MyTimer() {
        t = new Thread(() -> {
            while (true) {
                try {
                    synchronized (this) {
                        MyTask myTask = queue.take();
                        long curTime = System.currentTimeMillis();
                        if (curTime < myTask.getTime()) {
                            //还没到点,先不执行
                            queue.put(myTask);
                            //设置wait的等待时间,即队首元素与现在时间的差值
                            this.wait(myTask.getTime() - curTime);
                        } else {
                            //时间到了,执行任务
                            myTask.run();
                        }
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        t.start();
    }

    /**
     * 该方法本身很简单,只是单纯把任务放到队列中,指定两个参数
     *
     * @param runnable 任务内容
     * @param after    任务在多少毫秒之后执行
     */
    public void schedule(Runnable runnable, long after) {
        //注意这里时间上的换算
        MyTask myTask = new MyTask(runnable, System.currentTimeMillis() + after);
        queue.put(myTask);
        //只要有新的线程加入就会唤醒wait
        synchronized (this) {
            this.notify();
        }
    }
}

测试:

/**
 * 分别创建三个任务,三个任务的执行时间分别是1000 2000 3000
 * 如果打印结果分别按照顺序执行则正确
 */
public class ThreadTest {
    public static void main(String[] args) {
        MyTimer myTimer = new MyTimer();
        myTimer.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("任务1");
            }
        }, 1000);
        myTimer.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("任务2");
            }
        }, 2000);
        myTimer.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("任务3");
            }
        }, 3000);
    }
}

总结:

  1. 这里会按照设定时间的先后打印任务,发现此时打印完三个任务之后程序没有停止,因为在刚刚模拟实现的定时器中采用了while(true)的操作,会一直循环从阻塞队列中获取任务,队列为空就会发生阻塞等待(标准库中的定时器也是想相同的逻辑),默认为前台线程;

  1. 因为采用了优先级队列的数据结构,所以对自己定义的MyTask类一定要设置优先级,需要实现Compareable接口重写compareTo方法;

  1. 这里一定要用wait方法设置一个时间爱参数避免“死等”的现象,从而浪费CPU资源

在schedule方法中设置notify方法来唤醒,每次注册一个任务之后就进行判读时间是否到了是否需要执行

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

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

相关文章

分享|UWB使用频段大幅收窄,新标准对于行业发展是好是坏?

近日&#xff0c;工信部无线电管理局发布了《超宽带&#xff08;UWB&#xff09;设备无线电管理规定&#xff08;征求意见稿&#xff09;》&#xff08;以下简称“新版《规定》”&#xff09;。 根据新版《规定》&#xff0c;未来国内UWB技术的使用频段为&#xff1a;7235-875…

seo的基本知识(概述网站内部优化和外部优化)

了解网站外部优化的4大重点 网站优化的时候都会重视网站的外部优化&#xff0c;所以网站外部优化的4大重点&#xff01;今天就来和大家说一说&#xff01; 1.高质量的内容和外链 未来的SEO道路高质量的有价值的内容是非常重要的&#xff0c;还有就是高质量的外链也是重要之…

北大硕士LeetCode算法专题课-查找相关问题

黑马算法面试专题 北大硕士LeetCode算法专题课-字符串相关问题 北大硕士LeetCode算法专题课-数组相关问题_​​​​​​ 北大硕士LeetCode算法专题课-基础算法查找_ 北大硕士LeetCode算法专题课-基础算法之排序_客 北大硕士LeetCode算法专题课---算法复杂度介绍_…

Neo4j框架学习之一安装和使用

文章目录1、何为Neo4j2、安装和使用2.1 安装2.2 基础概念1、何为Neo4j ​ Neo4j是一个高性能的NOSQL图形数据库&#xff0c;是一个嵌入式的、基于磁盘的&#xff0c;数据结果为网格(图)、具备完全的事务特性的Java持久化引擎。 数据结构 ​ 在一个图中包含两种基本的数据类型…

从浏览器里输入URL构建你的前端知识体系

嗨&#xff01;我是团子&#xff0c;好久不见~ 记得22年寒假复习八股的时候&#xff0c;一直在苦恼怎样才能把八股的内容真正的转换为自己的知识。毕竟光靠死记硬背每个知识点&#xff0c;是不能在面试中给面试官留下不错的印象的。后面在整理《浏览器里输入URL后发生了什么》…

Stellarium 1.2 正式发布

导读Stellarium 1.2 已发布。Stellarium 是一款免费开源 GPL&#xff08;自由软件基金会 GNU 通用公共许可证&#xff09;软件&#xff0c;它使用 OpenGL 图形接口对星空进行实时渲染。 软件可以模拟肉眼、双筒望远镜和小型天文等观察天空&#xff0c;根据观测者所处时间和位置…

项目管理:项目经理如何创建项目日程计划表

当项目经理接手项目后&#xff0c;要做好项目的日程安排&#xff0c;这是决定项目是否成功完成的最重要任务之一。 项目经理都希望项目按照制定好的进度计划完工&#xff0c;但在实际的情况中&#xff0c;总会有那么一两个项目会出现进度延迟的情况&#xff0c;管理者可以使用…

忆享科技戟星安全实验室|OSS的STS模式授权案例

戟星安全实验室忆享科技旗下高端的网络安全攻防服务团队.安服内容包括渗透测试、代码审计、应急响应、漏洞研究、威胁情报、安全运维、攻防演练等。本文约957字&#xff0c;阅读约需3分钟。前言《漏洞挖掘系列》将作为一个期刊持续更新&#xff0c;我们会将项目中所遇到的觉得有…

图像编辑Photoshop 2023中文新

Photoshop2023从照片编辑和合成到数字绘画、动画和图形设计-只要能想到&#xff0c;就能在Photoshop中创作出来。相信大家都有在用之前的版本&#xff0c;这款软件功能丰富&#xff0c;实用性很强&#xff0c;有着大量的功能用户都可以用上&#xff0c;不管是美化还是滤镜&…

基于冲突搜索(CBS)的多智能体路径寻优(MAPF)

1 背景 1.1 问题描述 多智能体路径寻优( Multi-Agent Path Finding&#xff0c;MAPF )问题由一个无向无权图G ( V &#xff0c;E )和一组k个智能体组成&#xff0c;其中智能体有起始点和目标点。时间被离散化为时间步。在连续的时间步之间&#xff0c;每个智能体既可以移动到…

Kafka生产者——消息发送流程,同步、异步发送API

生产者消息发送流程 发送原理 Kafka的Producer发送消息采用的是异步发送的方式。 在消息发送的过程中&#xff0c;涉及到了两个线程:main线程和Sender线程&#xff0c;以及一个线程共享变量:RecordAccumulator。 ①main线程中创建了一个双端队列RecordAccumulator&#xff0c…

Spring Boot 创建和使用

Spring Boot 创建和使用一、什么是 Spring Boot二、Spring Boot 优点三、Spring Boot 项目创建3.1 使用 Idea 创建验证3.2 网页版创建四、项目目录介绍五、约定大于配置 (重要)5.1 启动类5.2 自定义类在目录中的位置一、什么是 Spring Boot Spring 的诞⽣是为了简化 Java 程序…

《架构300讲》学习笔记(51-100)

前言 内容来自B站IT老齐架构300讲内容。 053动静分离 静态数据&#xff1a;无个性化的数据&#xff0c;静态文件&#xff0c;低频变动的数据。 动态数据&#xff1a;个性化推荐&#xff0c;高频写。 有效的区分页面中的动静数据是优化的关键前提。 页面伪静态化技术&#x…

【Leetcode】308. 二维区域和检索 - 可变

一、题目 1、题目描述 给你一个 2D 矩阵 matrix&#xff0c;请计算出从左上角 (row1, col1) 到右下角 (row2, col2) 组成的矩形中所有元素的和。 实现 NumMatrix 类&#xff1a; NumMatrix(int[][] matrix) 用整数矩阵 matrix 初始化对象。void update(int row, int col, i…

OpenCv相机标定——圆形标定板标定

提取角点时与黑白棋盘格差别主要在于寻找角点的函数&#xff0c;只需将第一章内第二段代码 ret, corners1 cv.findChessboardCorners(img_gray, (w, h)) # 寻找内角点改为 ret, corners1 cv.findCirclesGrid(img_gray, (w, h)) # 寻找内角点&#xff0c;更详细的内容参考第一…

盘点| 能够实现小程序开发提效的框架/工具有这些

近年来&#xff0c;为了研发效率的提升&#xff0c;技术高频革新&#xff0c;开发者们纷纷表示&#xff1a;“好是好&#xff0c;就是快学不动了&#xff01;”。开发者们在不断学习新语言、框架、工具等内容的同时&#xff0c;也在担心所学是否真正有用。而小程序其实能够帮助…

9、Javaweb_http响应概念Response+验证码案例ServletContext+文件下载

HTTP协议&#xff1a; 1. 请求消息&#xff1a;客户端发送给服务器端的数据 * 数据格式&#xff1a; 1. 请求行 2. 请求头 3. 请求空行 4. 请求体 2. 响应消息&#xff1a;服务器端发送给客户端的数据 * 数据格式&#xff1a; …

Nginx 常用配置、操作详解

学习每个技术都要有目标&#xff0c;比如说要源码精通gRPC实现原理&#xff0c;要熟练应用Prometheus、Gin&#xff0c;以及Nginx&#xff0c;Nginx个人定位目标是不需要深入了解技术原理、更不要阅读源码&#xff0c;只需要在自己使用的时候能通过本文章快速检索就够了。 在看…

Graphing calculator PRO

Graphing calculator PRO计算器是一个专业的计算器&#xff0c;它也是编译的&#xff0c;也是学生和学生需要的工具。该程序旨在取代大型和昂贵的图形计算。此外&#xff0c;它在手机或广告牌显示屏上以更高的质量显示计算&#xff0c;这使其更易于理解。Mathlab提供的计算器是…

【云原生进阶之容器】第三章List-Watch机制3.1节-- List-Watch机制剖析

1 list-watch机制 1.1 list-watch介绍 Kubernetes 是通过 List-Watch 的机制进行每个组件的协作,保持数据同步的,每个组件之间的设计实现了解耦。 用户是通过 kubectl 根据配置文件,向 APIServer 发送命令,在 Node 节点上面建立 Pod 和 Container。 APIServer 经过 API 调…