返回顶部
首页 > 资讯 > 后端开发 > JAVA >【Java|多线程与高并发】定时器(Timer)详解
  • 879
分享到

【Java|多线程与高并发】定时器(Timer)详解

javajvm开发语言 2023-10-05 13:10:04 879人浏览 泡泡鱼
摘要

文章目录 1. 前言2. 定时器的基本使用3. 实现定时器4. 优化上述的定时器代码5. 总结 1. 前言 在Java中,定时器Timer类是用于执行定时任务的工具类。它允许你安排一个

文章目录

1. 前言

在Java中,定时器Timer类是用于执行定时任务的工具类。它允许你安排一个任务在未来的某个时间点执行,或者以固定的时间间隔重复执行。

服务器开发中,客户端向服务器发送请求,然后等待服务器响应. 但服务器什么时候返回响应,并不确定. 但也不能让客户端一直等下去, 如果一直死等,就没有意义了. 因此通常客户端会通过定时器设置一个"等待的最长时间".
在这里插入图片描述

2. 定时器的基本使用

Java的标准库库中就给我们提供了一个定时器Timer类

可以看到Timer这个类在很多包里面都有,注意要选择java.util里的

在这里插入图片描述

其中在Timer类中有一个十分重要的方法- schedule()方法
在这里插入图片描述

形参:

  • task:要执行的任务,必须是TimerTask的子类,可以通过继承TimerTask类并重写run()方法来定义具体的任务逻辑。
  • time:指定任务执行的时间,类型为java.util.Date

当然一个Timer类中也可以执行设置多个任务.

示例:

public class Demo17 {    public static void main(String[] args) {        Timer timer = new Timer();        timer.schedule(new TimerTask() {            @Override            public void run() {                System.out.println("1s!");            }        },1000);                timer.schedule(new TimerTask() {            @Override            public void run() {                System.out.println("2s!");            }        },2000);                timer.schedule(new TimerTask() {            @Override            public void run() {                System.out.println("3s!");            }        },3000);    }}

运行结果:

在这里插入图片描述
仔细观察运行结果,会发现这个程序有些问题,为什么程序执行完了,进程没有退出呢?

是因为Timer内部需要一组线程来执行注册任务,这里的线程是前台线程,会影响进程的退出

3. 实现定时器

实现定时器,最主要的就是实现里面的schedule方法

class MyTask{    // 要执行的任务    private Runnable runnable;    // 时间    private long time;    public MyTask(Runnable runnable, long time) {        this.runnable = runnable;        this.time = System.currentTimeMillis() + time;    }}public class MyTimer {    public void schedule(Runnable runnable, long time){        MyTask myTask = new MyTask(runnable,time);    }}

System.currentTimeMillis()是Java中的一个静态方法,用于获取当前时间的毫秒数。

描述一个任务,以及多久后执行定时器的第一步完成了

接下来就是如何让这个定时器能够管理多个任务,例如上述示例中输出1s,2s,3s的那个示例一样

关于如何管理这些任务,我们肯定是想让设置时间短的任务先执行,但是在设置任务时,不一定会按照时间从小到大的顺序去进行放入. 这时候就要使用到 优先级队列(PriorityQueue)

但是优先级队列并不是线程安全的, 在多线程环境下使用优先级队列可能会出现问题,我们可以使用阻塞队列
不要忘了,我们可以创建一个带有优先级的阻塞队列
在这里插入图片描述

将任务添加到阻塞队列中即可.

但是优先级队列的对象的类型必须是可比较的. 我们可以让Mytask实现Comparable接口,实现里面的compareTo方法. 比较的规则就是时间,时间小的优先级高.

接下来就要检查队首任务的时间是否到了,时间到了就要执行任务. 可以单独创建一个扫描线程来进行检查.

完整代码:

import java.util.concurrent.BlockingQueue;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 = System.currentTimeMillis() + time;    }    public Runnable getRunnable() {        return runnable;    }    public long getTime() {        return time;    }    @Override    public int compareTo(MyTask o) {        return (int) (this.time - o.time);    }}public class MyTimer {    private BlockingQueue<MyTask> blockingQueue = new PriorityBlockingQueue<>();    public MyTimer() {        Thread t = new Thread(()->{           while(true){               try {                   MyTask myTask = blockingQueue.take();                   // 当前时间是否大于等于要执行任务的时间                   if (System.currentTimeMillis() >= myTask.getTime()){                       // 时间到了 执行任务                       myTask.getRunnable().run();                   }else {                       // 时间没到,再把任务放回阻塞队列中                       blockingQueue.put(myTask);                   }               } catch (InterruptedException e) {                   throw new RuntimeException(e);               }           }        });        t.start();    }    public void schedule(Runnable runnable, long time) throws InterruptedException {        MyTask myTask = new MyTask(runnable,time);        blockingQueue.put(myTask);    }}

测试代码:

public class Demo18 {    public static void main(String[] args) throws InterruptedException {        MyTimer myTimer = new MyTimer();        myTimer.schedule(new Runnable() {            @Override            public void run() {                System.out.println("2s");            }        },2000);        myTimer.schedule(new Runnable() {            @Override            public void run() {                System.out.println("1s");            }        },1000);        myTimer.schedule(new Runnable() {            @Override            public void run() {                System.out.println("3s");            }        },3000);    }}

运行结果:
在这里插入图片描述
结果没有问题.

4. 优化上述的定时器代码

但仔细思考上述代码中还存在一个问题:
在这里插入图片描述
这里的条件是while(true),说明程序会一直进行这里的循环, 这也是"忙等"

"忙等"是指一个线程在等待某个条件满足时,不断地进行无效的循环检查,而不释放CPU资源给其他线程执行。这种方式会浪费CPU资源,并且可能导致性能下降。

针对这个问题,我们可以使用 wait和notify来解决这个问题

通过使用waitnotify,对MyTimer这个类进行优化:

public class MyTimer {    private BlockingQueue<MyTask> blockingQueue = new PriorityBlockingQueue<>();    private Object locker = new Object();    public MyTimer() {        Thread t = new Thread(()->{           while(true){               try {                   MyTask myTask = blockingQueue.take();                   // 当前时间是否大于等于要执行任务的时间                   if (System.currentTimeMillis() >= myTask.getTime()){                       // 时间到了 执行任务                       myTask.getRunnable().run();                   }else {                       // 时间没到,再把任务放回阻塞队列中                       blockingQueue.put(myTask);                       // 进行等待                       synchronized (locker) {                           locker.wait(myTask.getTime()-System.currentTimeMillis());                       }                   }               } catch (InterruptedException e) {                   throw new RuntimeException(e);               }           }        });        t.start();    }    public void schedule(Runnable runnable, long time) throws InterruptedException {        MyTask myTask = new MyTask(runnable,time);        blockingQueue.put(myTask);        synchronized (locker) {            locker.notify();        }    }}

虽然解决了"忙等"问题,但是又带来了新的问题.

如果扫描线程再取出队首任务(10分钟后要执行)时,线程切换,执行schedule方法,新增任务(5分钟后执行)然后执行notify,但此时并没有通知线程并没有意义,因为扫描线程刚执行完take,并没有执行到wait,然后扫描线程继续执行,进行wait,等待10分钟. 这样就会把刚才新增的5分钟后执行的任务给错过了.

对于上述问题 产生的原因还是因为""的粒度不够大, 这些操作不是原子的,只需放大锁的粒度即可

public class MyTimer {    private BlockingQueue blockingQueue = new PriorityBlockingQueue<>();    private Object locker = new Object();    public MyTimer() {        Thread t = new Thread(()->{           while(true){               try {                   synchronized (locker) {                       MyTask myTask = blockingQueue.take();                       // 当前时间是否大于等于要执行任务的时间                       if (System.currentTimeMillis() >= myTask.getTime()){                           // 时间到了 执行任务                           myTask.getRunnable().run();                       }else {                           // 时间没到,再把任务放回阻塞队列中                           blockingQueue.put(myTask);                           // 进行等待                           locker.wait(myTask.getTime()-System.currentTimeMillis());                       }                   }               } catch (InterruptedException e) {                   throw new RuntimeException(e);               }           }        });        t.start();    }    public void schedule(Runnable runnable, long time) throws InterruptedException {        MyTask myTask = new MyTask(runnable,time);        blockingQueue.put(myTask);        synchronized (locker) {            locker.notify();        }    }}

在这里插入图片描述
上述为了解决"忙等"问题,使用wait和notify进行优化,而在优化过程因为synchronized加锁的范围不一样,又带来了新的问题. 因此多线程问题很复杂,加锁的范围,线程的切换都会影响程序的执行效果.

5. 总结

文章主要介绍了定时器的基本使用,以及自定义实现定时器,实现一个定时器并不难.但如果要想将定时器实现的更好,也不是一件容易的事. 毕竟多线程环境中,很容易出现各种意想不到的问题.
在这里插入图片描述

感谢你的观看!希望这篇文章能帮到你!
专栏: 《从零开始的Java学习之旅》在不断更新中,欢迎订阅!
“愿与君共勉,携手共进!”
在这里插入图片描述

来源地址:https://blog.csdn.net/m0_63463510/article/details/131376176

--结束END--

本文标题: 【Java|多线程与高并发】定时器(Timer)详解

本文链接: https://lsjlt.com/news/423743.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • 【Java|多线程与高并发】定时器(Timer)详解
    文章目录 1. 前言2. 定时器的基本使用3. 实现定时器4. 优化上述的定时器代码5. 总结 1. 前言 在Java中,定时器Timer类是用于执行定时任务的工具类。它允许你安排一个...
    99+
    2023-10-05
    java jvm 开发语言
  • 详解Java多线程与并发
    目录一、进程与线程二、并发与并行1、线程安全问题2、共享内存不可见性问题三、创建线程1、继承Thread类2、实现Runable接口3、实现Callable接口四、Thread类详解...
    99+
    2024-04-02
  • java的多线程高并发详解
    目录1.JMM数据原子操作2.来看volatile关键字3.并发编程三大特性4.双锁判断机制创建单例模式5.synchronized关键字6.AtomicIntger原子操作7.锁优...
    99+
    2024-04-02
  • Java线程之Timer定时器
    定时/计划功能主要使用的就是Timer对象,它在内部还是使用多线程的方式进行处理,所以它和线程技术还是有非常大的关联。Timer类主要作用就是设置计划任务,但封装任务的类却是TimerTask类。TimerTask类是一个抽象类。执行任务的...
    99+
    2018-03-22
    java教程 Java 线程 Timer 定时器
  • Java多线程 - 定时器-并发与并行-线程生命周期
    文章目录 多线程补充定时器并发和并行线程的生命周期 多线程补充 定时器 定时器介绍: 定时器是一种控制任务延时调用,或者周期调用的技术。 作用:闹钟、定时邮件发送。 定时...
    99+
    2023-09-25
    java jvm 开发语言
  • Java多线程之定时器Timer的实现
    目录标准库中的Timer模拟实现Timer标准库中的Timer 标准库中有一个Timer类,java.util.Timer,核心方法为schedule,schedule有两个参数,第...
    99+
    2022-11-13
    Java 多线程 定时器Timer Java 定时器Timer Java 定时器
  • Java中的定时器Timer详解
    目录总结简单来说,定时器就相当于一个“闹钟”,给定时器设定一个任务,约定这个任务在xxx时间之后执行~ Timer类提供了一个核心接口,schedule(安排) 指定一个任...
    99+
    2024-04-02
  • Python多线程与高并发
    主要讲解了关于Python多线程的一些例子和高并发的一些应用场景# -*- coding: utf-8 -*- # @Author: Clarence...
    99+
    2023-01-31
    多线程 Python
  • Java多线程定时器Timer原理的示例分析
    这篇文章给大家分享的是有关Java多线程定时器Timer原理的示例分析的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。Timer的schedule(TimeTask task, Date time)的使用该方法的作...
    99+
    2023-05-30
    java
  • java 多线程与并发之volatile详解分析
    目录CPU、内存、缓存的关系CPU缓存什么是CPU缓存为什么要有多级CPU CacheJava内存模型(Java Memory Model,JMM)JMM导致的并发安全问题可见性原子...
    99+
    2024-04-02
  • java高并发之线程组详解
    目录线程组创建线程关联线程组为线程组指定父线程组根线程组批量停止线程总结线程组 我们可以把线程归属到某个线程组中,线程组可以包含多个线程以及线程组,线程和线程组组成了父子关系,是个树...
    99+
    2024-04-02
  • Java多线程案例之定时器详解
    目录一.什么是定时器二.标准库中的定时器(timer)2.1什么是定时器2.2定时器的使用三.实现定时器3.1什么是定时器3.2最终实现代码一.什么是定时器 定时器也是软件开发中的一...
    99+
    2022-11-13
    Java多线程 定时器 Java 定时器 Java 多线程
  • Java多线程并发FutureTask使用详解
    目录基本使用代码分析继承关系FutureRunnableFutureFutureTask状态属性内部类构造方法检索 FutureTask 状态取消操作计算结果立刻获取结果或异常run...
    99+
    2024-04-02
  • Java 并发(多线程)超详细
    Java 并发 此文章已收录至项目 Developer-Knowledge-Base 信息来源 https://www.cnblogs.com/snow-flower/p/6114765.html j...
    99+
    2023-09-06
    java 开发语言
  • Java多线程并发AbstractQueuedSynchronizer详情
    目录AbstractQueuedSynchronizer核心思想为什么需要 AQS用法用法示例AQS 底层原理父类 AbstractOwnableSynchronizerCLH 队列...
    99+
    2024-04-02
  • Java 多线程并发 ReentrantReadWriteLock详情
    目录前言ReadWriteLockReentrantReadWriteLock 源码分析类关系SyncHoldCounterThreadLocalHoldCounter属性构造方法核...
    99+
    2024-04-02
  • Java多线程并发与并行和线程与进程案例
    目录一、并发与并行二、线程与进程三、创建线程类前言: 程序在没有跳转语句的前提下,都是由上至下依次执行,那现在想要设计一个程序,边打游戏边听歌,怎么设计? 要解决上述问题,咱们得使用...
    99+
    2024-04-02
  • Java多线程高并发中的Fork/Join框架机制详解
    1.Fork/Join框架简介 Fork/Join 它可以将一个大的任务拆分成多个子任务进行并行处理,最后将子任务结果合并成最后的计算结果,并进行输出。Fork/Join 框架要完成...
    99+
    2024-04-02
  • java高并发的用户线程和守护线程详解
    目录程序只有守护线程时,系统会自动退出设置守护线程,需要在start()方法之前进行线程daemon的默认值总结守护线程是一种特殊的线程,在后台默默地完成一些系统性的服务,比如垃圾回...
    99+
    2024-04-02
  • 详解Python的多线程定时器threading.Timer
    threading.Timer 一次timer只生效一次,不会反复循环,如果实现循环触发,代码如下: import time import threading def createT...
    99+
    2024-04-02
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作