Java多线程案例之定时器详解

目录
  • 一.什么是定时器
  • 二.标准库中的定时器(timer)
    • 2.1什么是定时器
    • 2.2定时器的使用
  • 三.实现定时器
    • 3.1什么是定时器
    • 3.2最终实现代码

一.什么是定时器

定时器也是软件开发中的一个重要组件. 类似于一个 “闹钟”. 达到一个设定的时间之后, 就执行某个指定好的代码

定时器是一种实际开发中非常常用的组件,我们举几个例子:

1.比如网络通信中, 如果对方 500ms 内没有返回数据, 则断开连接尝试重连

2.比如一个 Map, 希望里面的某个 key 在 3s 之后过期(自动删除)

以上类似于这样的场景就需要用到定时器

二.标准库中的定时器(timer)

2.1什么是定时器

标准库中供了一个 Timer 类. Timer 类的核心方法为 schedule ,schedule 包含两个参数. 第一个参数指定即将要执行的任务代码TimerTask, 第二个参数指定多长时间之后执行 (单位为毫秒).

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

2.2定时器的使用

Timer的构造方法

构造方法 说明
public Timer() 无参数构造方法,默认定时器关联的线程不是守护线程,线程名字也是默认值
public Timer(boolean isDaemon) 指定定时器中关联的线程是否为守护线程,如果是,参数为true
public Timer(String name) 指定定时器关联线程名称,线程类型默认为非守护线程
public Timer(String name, boolean isDaemon) 指定定时器关联线程名和线程类型

Timer方法

方法 说明
public void schedule (TimerTask task, long delay) 指定任务,延迟多久执行该任务
public void schedule(TimerTask task, Date time) 指定任务,指定任务的执行时间
public void schedule(TimerTask task, long delay, long period) 连续执行指定任务,延迟时间,连续执行任务的时间间隔,毫秒为单位
public void schedule(TimerTask task, Date firstTime, long period) 连续执行指定任务,第一次任务的执行时间,连续执行任务的时间间隔
public void scheduleAtFixedRate(TimerTask task, Date firstTime, long period) 连续执行指定任务,第一次任务的执行时间,连续执行任务的时间间隔
public void scheduleAtFixedRate(TimerTask task, long delay, long period) 连续执行指定任务,延迟时间,连续执行任务的时间间隔,毫秒为单位
public void cancel() 终止定时器所有任务,终止执行的任务不受影响

TimerTask是专门来实现Runnable接口的

下面我们会实现一下定时器,我们就不用TimerTask了,我们直接使用Runnable,因为TimerTask实现了Runnable接口,所以后面测试我们自己所写的schedule方法时,也可以传入TimerTask类型的引用,既然是简单地实现,那就不实现连续执行的功能了。.

public class Test {
    public static void main(String[] args){
        Timer timer = new Timer();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("执行线程在5s后执行");
            }
        },5000);
        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);
    }
}

三.实现定时器

3.1什么是定时器

定时器的构成:一个带优先级的阻塞队列

为啥要带优先级呢?

因为阻塞队列中的任务都有各自的执行时刻 (delay). 最先执行的任务一定是 delay 最小的. 使用带优先级的队列就可以高效的把这个 delay 最小的任务找出来.

1.队列中的每个元素是一个 Task 对象,Task 中带有一个时间属性, 队首元素就是即将同时有一个 worker 线程一直扫描队首元素, 看队首元素是否需要执行

class MyTask implements Comparable<MyTask>{
    //执行的时间戳
    private long time;
    //接受具体任务
    private Runnable runnable;
    //创建MyTask构造方法
    public MyTask(Runnable runnable,long time) {
        //通过currentTimeMillis来获取time 中存的是绝对时间, 超过这个时间的任务就应该被执行
        this.time = System.currentTimeMillis()+time;
        this.runnable = runnable;
    }
    //执行任务
    public void run(){
        this.runnable.run();
    }
    //提供对外time
    public long getTime() {
        return time;
    }
    //执行comparable接口来进行时间的比较,并将time的long类型转换为int类型
    @Override
    public int compareTo(MyTask o) {
        return (int)(this.time-o.time);
    }
}

Timer 实例中, 通过 PriorityBlockingQueue 来组织若干个 Task 对象.通过 schedule 来往队列中插入一个个 Task 对象.

class MyTimer{
    // 定时器内部要能够存放多个任务
    private PriorityBlockingQueue<MyTask> queue = new PriorityBlockingQueue<>();
    //为锁创建一个对象
    Object locker = new Object();
    public void schedule(Runnable runnable, long delay) {
        MyTask task = new MyTask(runnable, delay);
        queue.put(task);
        // 每次任务插入成功之后, 都唤醒一下扫描线程, 让线程重新检查一下队首的任务看是否时间到要执行~~
        synchronized (locker) {
            locker.notify();
        }
    }

Timer 类中存在一个 worker 线程, 一直不停的扫描队首元素, 看看是否能执行这个任务.所谓 “能执行” 指的是该任务设定的时间已经到达了

class MyTimer{
    // 定时器内部要能够存放多个任务
    private PriorityBlockingQueue<MyTask> queue = new PriorityBlockingQueue<>();
    //为锁创建一个对象
    Object locker = new Object();
    public void schedule(Runnable runnable, long delay) {
        MyTask task = new MyTask(runnable, delay);
        queue.put(task);
        // 每次任务插入成功之后, 都唤醒一下扫描线程, 让线程重新检查一下队首的任务看是否时间到要执行~~
        synchronized (locker) {
            locker.notify();
        }
    }
    public MyTimer() {
        Thread t = new Thread(() -> {
            while (true) {
                try {
                    // 先取出队首元素
                    MyTask task = queue.take();
                    // 再比较一下看看当前这个任务时间到了没?
                    long curTime = System.currentTimeMillis();
                    if (curTime < task.getTime()) {
                        // 时间没到, 把任务再塞回到队列中.
                        queue.put(task);
                        // 指定一个等待时间,防止有的线程需要等待时间很长,但是线程一直运行等待时间到来执行,这样会占有CPU占有资源
                        synchronized (locker) {
                            locker.wait(task.getTime() - curTime);
                        }
                    } else {
                        // 时间到了, 执行这个任务
                        task.run();
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        t.start();
    }
}

3.2最终实现代码

package thread;

import java.util.PriorityQueue;
import java.util.concurrent.PriorityBlockingQueue;

// 创建一个类, 表示一个任务.
class MyTask implements Comparable<MyTask> {
    // 任务具体要干啥
    private Runnable runnable;
    // 任务具体啥时候干. 保存任务要执行的毫秒级时间戳
    private long time;

    // after 是一个时间间隔. 不是绝对的时间戳的值
    public MyTask(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(MyTask o) {
        // 到底是谁见谁, 才是一个时间小的在前? 需要咱们背下来.
        return (int) (this.time - o.time);
    }
}

class MyTimer {
    // 定时器内部要能够存放多个任务
    private PriorityBlockingQueue<MyTask> queue = new PriorityBlockingQueue<>();

    public void schedule(Runnable runnable, long delay) {
        MyTask task = new MyTask(runnable, delay);
        queue.put(task);
        // 每次任务插入成功之后, 都唤醒一下扫描线程, 让线程重新检查一下队首的任务看是否时间到要执行~~
        synchronized (locker) {
            locker.notify();
        }
    }

    private Object locker = new Object();

    public MyTimer() {
        Thread t = new Thread(() -> {
            while (true) {
                try {
                    // 先取出队首元素
                    MyTask task = queue.take();
                    // 再比较一下看看当前这个任务时间到了没?
                    long curTime = System.currentTimeMillis();
                    if (curTime < task.getTime()) {
                        // 时间没到, 把任务再塞回到队列中.
                        queue.put(task);
                        // 指定一个等待时间
                        synchronized (locker) {
                            locker.wait(task.getTime() - curTime);
                        }
                    } else {
                        // 时间到了, 执行这个任务
                        task.run();
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        t.start();
    }
}

public class Test {
    public static void main(String[] args) {
        MyTimer myTimer = new MyTimer();
        myTimer.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("hello timer!");
            }
        }, 3000);
        System.out.println("main");
    }
}

以上就是Java多线程案例之定时器详解的详细内容,更多关于Java多线程 定时器的资料请关注我们其它相关文章!

(0)

相关推荐

  • Java多线程定时器Timer原理及实现

    前言 定时/计划功能在Java应用的各个领域都使用得非常多,比方说Web层面,可能一个项目要定时采集话单.定时更新某些缓存.定时清理一批不活跃用户等等.定时计划任务功能在Java中主要使用的就是Timer对象,它在内部使用多线程方式进行处理,所以它和多线程技术关联还是相当大的.那和ThreadLocal一样,还是先讲原理再讲使用,Timer的实现原理不难,就简单扫一下就好了. Timer的schedule(TimeTask task, Date time)的使用 该方法的作用是在执行的日期执行一

  • java实现多线程之定时器任务

    在Java中Timer是java.util包中的一个工具类,提供了定时器的功能.我们可以创建一个Timer对象,然后调用其schedule方法在某个特定的时间去执行一个特定的任务.并且你可以让其以特定频率一直执行某个任务,这个任务是用TimerTask来描述的,我们只需要将要进行的操作写在TimerTask类的run方法中即可.先附上两个小例子一遍让读者了解什么是定时器.接着再分析其中的一些源码实现. 第一个小例子: package com.zkn.newlearn.thread; import

  • Java多线程(单例模式,阻塞队列,定时器,线程池)详解

    目录 1. 单例模式(singleton pattern) 1.1 懒汉模式 1.2 饿汉模式 2 阻塞队列(blocking queue) 2.1 阻塞队列 2.2 生产者消费者模型 2.3 标准库中的阻塞队列 2.4 实现阻塞队列 3. 定时器 3.1 标准库中的定时器 3.2 实现定时器 4 线程池 4.1 标准库中的线程池 4.2 Executors 创建线程池的几种方式 4.3 利用线程池 创建多线程计算fib 数 4.4 实现线程池 1. 单例模式(singleton pattern

  • Java多线程(单例模式,堵塞队列,定时器)详解

    目录 一.单例模式 饿汉模式 懒汉模式 懒汉模式 二.堵塞队列 实现BlockingQueue 三.定时器 总结 一.单例模式 单例模式是一种设计模式,针对一些特定的场景,研究出对应的解决方案,.有些对象在代码中只应该有一个实例,单例模式就是强制某个类只能有一个实例. 单例模式的实现,主要依托于static关键字(被static 修饰的成员,静态成员,把当前的成员变成类属性而不是实例属性~)每个类对象只有一份 单例模式实现有两种,饿汉模式和懒汉模式 饿汉模式 饿汉模式实现:实例创建出现在"类加载

  • java多线程之定时器Timer的使用详解

    定时的功能我们在手机上见得比较多,比如定时清理垃圾,闹钟,等等.定时功能在java中主要使用的就是Timer对象,他在内部使用的就是多线程的技术. Time类主要负责完成定时计划任务的功能,就是在指定的时间的开始执行某个任务. Timer类的作用是设置计划任务,而封装任务内容的类是TimerTask类.此类是一个抽象类,继承需要实现一个run方法. 通过查文档我们看到Timer有以下几个构造函数: Timer的方法以下这么多: 下面我们通过定时器来完成一个简单功能,就是在运行项目三秒后,在控制台

  • Java多线程案例之定时器详解

    目录 一.什么是定时器 二.标准库中的定时器(timer) 2.1什么是定时器 2.2定时器的使用 三.实现定时器 3.1什么是定时器 3.2最终实现代码 一.什么是定时器 定时器也是软件开发中的一个重要组件. 类似于一个 “闹钟”. 达到一个设定的时间之后, 就执行某个指定好的代码 定时器是一种实际开发中非常常用的组件,我们举几个例子: 1.比如网络通信中, 如果对方 500ms 内没有返回数据, 则断开连接尝试重连 2.比如一个 Map, 希望里面的某个 key 在 3s 之后过期(自动删除

  • Java多线程用法的实例详解

    Java多线程用法的实例详解 前言: 最全面的java多线程用法解析,如果你对Java的多线程机制并没有深入的研究,那么本文可以帮助你更透彻地理解Java多线程的原理以及使用方法. 1.创建线程 在Java中创建线程有两种方法:使用Thread类和使用Runnable接口.在使用Runnable接口时需要建立一个Thread实例.因此,无论是通过Thread类还是Runnable接口建立线程,都必须建立Thread类或它的子类的实例.Thread构造函数: public Thread( ); p

  • Java多线程通信实现方式详解

    这篇文章主要介绍了Java多线程通信实现方式详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 线程通信的方式: 1.共享变量 线程间通信可以通过发送信号,发送信号的一个简单方式是在共享对象的变量里设置信号值.线程A在一个同步块里设置boolean型成员变量hasDataToProcess为true,线程B也在同步代码块里读取hasDataToProcess这个成员变量.这个简单的例子使用了一个持有信号的对象,并提供了set和get方法. pu

  • Java多线程 线程状态原理详解

    这篇文章主要介绍了Java多线程 线程状态原理详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 java.lang.Thread.State枚举定义了6种线程状态. NEW: 尚未启动(start)的线程的线程状态 RUNNABLE: 运行状态,但线程可能正在JVM中执行,也可能在等待CPU调度 BLOCKED: 线程阻塞,等待监视器锁以进入同步代码块/方法 WAITING: 等待状态.使用以下不带超时的方式时会进入:Object.wait.

  • java多线程JUC常用辅助类详解

    1.countDownLatch 减法计数器:实现调用几次线程后,在触发另一个任务 简单代码实现: 举例说明:就像五个人在同一房间里,有一个看门的大爷,当五个人都出去后,他才能锁门,也就是说 执行5次出门这个动作的线程后,才出发了锁门的这个动作 import java.util.concurrent.CountDownLatch; /** * @program: juc * @description * @author: 不会编程的派大星 * @create: 2021-04-24 16:55

  • Java多线程解决龟兔赛跑问题详解

    目录 多线程4(龟兔赛跑-休眠线程) 1.题目 2.解题思路 3.代码详解 多线程4(龟兔赛跑-休眠线程) 1.题目 在龟兔赛跑中,领先的兔子因为通宵写博客,中途太累睡着了,跑输了乌龟.这个故事说明了兔子是爱学习的同学.咳咳,通宵是不可取的,大家别学. 实现:使用线程休眠模拟龟兔赛跑比赛 2.解题思路 创建一个类:RaceFrame,继承了JFrame.用来界面显示两个文本区域,用来输出乌龟和兔子的比赛记录,比赛开始按钮用来开始比赛. 编写内部类:Rabbit,该类实现了Runnable接口,在

  • java多线程中线程封闭详解

    线程封闭的概念 访问共享变量时,通常要使用同步,所以避免使用同步的方法就是减少共享数据的使用,这种技术就是线程封闭. 实现线程封闭的方法 1:ad-hoc线程封闭 这是完全靠实现者控制的线程封闭,他的线程封闭完全靠实现者实现.也是最糟糕的一种线程封闭.所以我们直接把他忽略掉吧. 2:栈封闭 栈封闭是我们编程当中遇到的最多的线程封闭.什么是栈封闭呢?简单的说就是局部变量.多个线程访问一个方法,此方法中的局部变量都会被拷贝一分儿到线程栈中.所以局部变量是不被多个线程所共享的,也就不会出现并发问题.所

  • JAVA多线程之方法 JOIN详解及实例代码

    JAVA多线程 JOIN 对于Java开发人员,多线程应该是必须熟练应用的知识点,特别是开发基于Java语言的产品.本文将深入浅出的表述Java多线程的知识点,在后续的系列里将侧重于Java5由Doug Lea教授提供的Concurrent并行包的设计思想以及具体实现与应用. 如何才能深入浅出呢,我的理解是带着问题,而不是泛泛的看.所以该系列基本以解决问题为主,当然我也非常希望读者能够提出更好的解决问题的方案以及提出更多的问题.由于水平有限,如果有什么错误之处,请大家提出,共同讨论,总之,我希望

  • Java多线程下载文件实例详解

    本文实例为大家分享了Java多线程下载文件的具体代码,供大家参考,具体内容如下 import java.io.File; import java.io.InputStream; import java.io.RandomAccessFile; import java.net.HttpURLConnection; import java.net.URL; public class MulThreadDownload { public static void main(String[] args)

  • Java多线程并发FutureTask使用详解

    目录 基本使用 代码分析 继承关系 Future RunnableFuture FutureTask 状态 属性 内部类 构造方法 检索 FutureTask 状态 取消操作 计算结果 立刻获取结果或异常 run 方法组 本文基于最新的 OpenJDK 代码,预计发行版本为 19 . Java 的多线程机制本质上能够完成两件事情,异步计算和并发.并发问题通过解决线程安全的一系列 API 来解决:而异步计算,常见的使用是 Runnable 和 Callable 配合线程使用. FutureTask

随机推荐