专栏首页后端Coderjava高级进阶|定时器的分析

java高级进阶|定时器的分析

以前写文章的时候忘了标记原创,导致最近整理文章的时候会发现不是原创的文章不给自己权限合入对应目录了,这也是自己后面慢慢开始注重这方面的积累了,读过我的文章的读者应该都知道我喜欢在文章的标题前加一个前缀"java进阶|xxx"。

到这里自己先试下水又重新给文章的标题又加了一个新的标签,因为我觉得这样的技术点,对于自己而言就是java高级进阶的系列,对于其他人而言自己不知道,所以后面自己觉得对于自己而言有分量的文章就以这样的前缀进行说明了。

在这里多说几句吧,自己原来写好很多篇文章但是文章的风格和现在的文章风格截然不同,之前代码充满了整个屏幕,只会增加一定的文字说明,相对于现在的文章而言,只能说一句,文字真少。今天早上本打算发一篇之前写好的文章时,觉得和自己这几天的源码文章风格不同,所以就立刻分享了TreeSet的源码分析文章。

其实每篇文章发出来之前自己总会去审查一遍,看看文字是否通顺,以及是否存在错别字等等吧,有的时候会把自己当时的心里话进行删减,有的时候又会增加一些和你们唠嗑的话语,或许这就是码农(我的)的文字表述吧。

今天自己就写了定时器的功能,每次写到这个单一的技术点时,总觉得我想写的内容还很多,所以慢慢来吧,既然18年就写过这样的代码放入了gitHub里,但是还是觉得有必要用一篇文章,重新站在自己的角度来看这个技术点。

定时器,可以看做是我们生活中很常见的闹钟或者具有提醒作用的装置,以我的角度就是定时去触发某种事件的发生,其业务场景也很常见,比如可以基于定时器去发邮件进行信息的分发,基于定时器去爬取数据这些都是很容易想到的点,所以这里就不具体举例子了,关于定时器的用途当你碰到业务场景再去做也不迟,至少自己去做过定时器收集数据的业务。

这里主要用来看下java原生的定时器,其实很简单,不过也需要去了解一下的,这里就写个示例程序简单使用一些吧。本次实现的功能就是在定时器启动的时,去打印当前线程的名称以及当前的时间。

import lombok.extern.slf4j.Slf4j;import java.text.SimpleDateFormat;import java.util.Date;import java.util.Timer;import java.util.TimerTask;import java.util.concurrent.TimeUnit;@Slf4jpublic class TimerTaskTest {    public static void main(String[] args) {        Timer timer = new Timer(true);//new出来一个定时器        timer.schedule(new TimerTask() {//基于lambda的方式去实现一个内部类            @Override            public void run() {            //获取当前线程的名称以及打印当前的时间,是不是很简单                System.out.println(Thread.currentThread().getName() + "当前的时间为" +                        new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));            }        }, new Date());        //这里就是为了阻挡主线程结束的一段sleep时间        try {            TimeUnit.SECONDS.sleep(1);        } catch (InterruptedException e) {            e.printStackTrace();            log.error("执行定时器时出现错误信息:{}", e.getMessage());        }    }}

这个示例程序中,设置了timer为守护线程,这样当主线程即jvm线程运行结束后就会结束整个程序的运行,如果不设置守护线程即成为了用户线程,这样程序就不会终止了,这里你自己可以试验一下。

不过上面的示例程序有个缺点就是只会执行一次,也就是单次定时器,这样的定时器可以理解为单次不可回收定时器,这是我给它起的名字,便于自己理解吧,一般我们使用定时器也会存在这样的情形,就是定期去执行,然后更新数据,更新缓存,爬取数据,定时提醒等等业务场景,基于这样的特点,timer自然就支持这样特性了,下面就再写一段示例程序,演示每隔1秒就会打印当前线程名称和当前时间信息的定时器。

import java.util.Date;import java.util.Timer;import java.util.TimerTask;public class TimerTaskTest2 {    public static void main(String[] args) {        Timer timer = new Timer();        timer.schedule(new TimerTask() {            @Override            public void run() {                System.out.println(Thread.currentThread().getName() + new Date());            }        }, new Date(), 1000);    }}

看下我执行上面的示例程序后控制台输出的日志信息,这里仅输出部分信息,自己可以执行看下就可以了。

Timer-0Wed May 27 21:14:29 CST 2020Timer-0Wed May 27 21:14:30 CST 2020Timer-0Wed May 27 21:14:31 CST 2020Timer-0Wed May 27 21:14:32 CST 2020Timer-0Wed May 27 21:14:33 CST 2020Timer-0Wed May 27 21:14:34 CST 2020Timer-0Wed May 27 21:14:35 CST 2020

下面我继续介绍定时器提供的另外一个功能延迟一段时间,然后在根据一定的速率执行定时器,这里就说下下面这个功能实现的意思吧,就是基于当前时间延迟1秒后,再以2秒的速率去触发定时器。

import java.util.Date;import java.util.Timer;import java.util.TimerTask;public class TimeTaskTest3 {    public static void main(String[] args) {        Timer timer = new Timer();        timer.schedule(new TimerTask() {            @Override            public void run() {                System.out.println(Thread.currentThread().getName() + new Date());            }        }, 1000, 2000);    }}

这里如果你想很明显的看到效果,你可以把1000改为5000即5秒,然后在去看执行的结果,这里就不给你们看控制台的信息了,自己执行下不香吗?其实我分享文章也是自己去理解的过程,毕竟这样对自己有点好处顺便分享一下。

关于定时器的功能就先讲述到这里,这里再继续拓展一下定时器Timer之外的内容,由于是之前仅用过一次而已,算是现学现卖吧,我看下自己是否可以理解一下(我已经理解了)写这句话时是因为我来检查自己写的是否存在错别字,?,因为每个示例程序都要经过自己的验证和判断。

那就是ScheduledExecutorService这样的定时器使用了,因为在我编写timer定时器示例程序时,它给我提示了,所以我就来看看它的使用方法了,毕竟我之前和同事用过它,仅此用过,因为我那个时候满脑子想的都是这个单机版的示例程序能不能真正大规模的使用呢,就没有将它编写到自己的gitHub仓库里面,所以这里想到它了就简单去写下吧,毕竟有文字的程序是符合自己目前文章风格的。

这里依然使用单次定时器和可定时循环操作的定时器集中方式来进行操作,首先我们看下单次执行的定时器示例程序吧。

import java.util.Date;import java.util.concurrent.ScheduledExecutorService;import java.util.concurrent.ScheduledThreadPoolExecutor;import java.util.concurrent.TimeUnit;
public class ScheduledExecutorServiceTest {    public static void main(String[] args) {        ScheduledExecutorService scheduledExecutorService = new ScheduledThreadPoolExecutor(1);        scheduledExecutorService.schedule(new Runnable() {            @Override            public void run() {                System.out.println(Thread.currentThread().getName() + new Date());            }        }, 1, TimeUnit.SECONDS);        scheduledExecutorService.shutdown();    }}

这里看下示例程序执行之后控制台输出的日志信息吧

pool-1-thread-1Wed May 27 21:50:16 CST 2020

毕竟作为一个coder我现在在这里写着自己喜欢的技术,因为自己想写的太多了,需要进行输出的也太多了,只能告诉自己慢慢输出,单次定时器的示例程序完成了之后就需要看下循环可利用的定时器的示例程序了。

import java.util.Date;import java.util.concurrent.ScheduledExecutorService;import java.util.concurrent.ScheduledThreadPoolExecutor;import java.util.concurrent.TimeUnit;
public class ScheduledExecutorServiceTest2 {    public static void main(String[] args) {        ScheduledExecutorService scheduledExecutorService = new ScheduledThreadPoolExecutor(1);        scheduledExecutorService.scheduleAtFixedRate(() -> System.out.println(Thread.currentThread().getName() + new Date()), 1, 2, TimeUnit.SECONDS);    }}

这里看下示例程序输出的日志信息吧,很直观的文字表述,看完之后的你理解了吧,其实我写的每一篇文章都是自己去思考过的,至少是自己的理解去写的,输出文章可以看做是自己做笔记的一种分享,只不过这种笔记上了互联网而已,这就是自己的一点小感悟。

pool-1-thread-1Wed May 27 21:56:31 CST 2020pool-1-thread-1Wed May 27 21:56:33 CST 2020pool-1-thread-1Wed May 27 21:56:35 CST 2020pool-1-thread-1Wed May 27 21:56:37 CST 2020pool-1-thread-1Wed May 27 21:56:39 CST 2020

如果执行定时器之后还想获取返回值信息,进行后面的操作,这里也提供了对应的方法进行,这里就看下这个示例程序吧,毕竟我也是刚接触到这个定时框架,即使之前用过一下,但是随着时间的流逝,自己也有点忘记了他的用法。

这里自己就是实现了,两秒之后可以获取数据的定时器,然后将获取的数据输出到控制台,来,看下示例程序吧。

import lombok.extern.slf4j.Slf4j;import java.util.concurrent.*;@Slf4jpublic class ScheduledExecutorServiceTest3 {    public static void main(String[] args) {        ScheduledExecutorService scheduledExecutorService = new ScheduledThreadPoolExecutor(1);        ScheduledFuture<String> schedule = scheduledExecutorService.schedule(new Callable<String>() {            @Override            public String call() throws Exception {                return collectData();            }            private String collectData() {                try {                    //这里简单模拟一下收集数据的业务耗时操作                    TimeUnit.SECONDS.sleep(2);                } catch (InterruptedException e) {                    log.error("错误信息:{}", e.getMessage());                }                return "hello 定时器";            }        }, 2, TimeUnit.SECONDS);        try {            String str = schedule.get();            System.out.println("str = " + str);        } catch (InterruptedException | ExecutionException e) {            log.error("获取数据错误:{}", e.getMessage());        }        scheduledExecutorService.shutdown();    }}

最后的最后,还是以往常的风格看下控制台信息的输出。其实可以进行返回值的输出,主要是实现了Callable接口,不理解这个接口的可以看我以往的文章,关于实现线程的三种方式的文章->java创建线程的三种方式,这里就不做过多的说明了。

str = hello 定时器

其实还有一个方法这里没有进行示例程序的编写,其实这里觉得还是写下吧,跑下示例程序,来个完整的示例程序。

import java.util.Date;import java.util.concurrent.ScheduledExecutorService;import java.util.concurrent.ScheduledThreadPoolExecutor;import java.util.concurrent.TimeUnit;
public class ScheduledExecutorServiceTest4 {    public static void main(String[] args) {        ScheduledExecutorService scheduledExecutorService = new ScheduledThreadPoolExecutor(1);        scheduledExecutorService.scheduleWithFixedDelay(() -> System.out.println(Thread.currentThread().getName() + new Date()), 1, 1, TimeUnit.SECONDS);    }}

这里想了解这个方法的可以自己运行下示例程序,日志信息就不输出了

本文分享自微信公众号 - WwpwW(gh_245290c1861a),作者:后端Coder

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2020-05-28

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • java自定义注解的使用

    不曾想,每个人都是这样经历过来的,不知你是否还记得在spring的xml配置文件里如何配置对象的场景?或许依然记忆犹新,或许早已抛开在脑后,等等吧。后面spri...

    后端Coder
  • java进阶|谈谈我对spring的理解

    上面讲述spring的基本介绍之后,就来谈谈学习spring这个框架的一点理解吧,我现在写这篇内容的时间节点是五月3号,对,这是五一期间写作的第二篇文章,但是现...

    后端Coder
  • java进阶|jdbc的用法

    大概是17年时用过jdbc进行操作数据库,到现在为止没有再用过jdbc去写过一个示例程序,即简单的增删改查操作,CRUD也不是那么容易写吧,后面就开始基...

    后端Coder
  • Python多线程编程基础3:创建线程与调用函数的区别

    在上一节Python多线程编程基础2:如何创建线程中,我们已经知道,创建线程并运行实际上也是执行一段代码,那么把这些代码封装到函数中之后,直接调用函数和创建线程...

    Python小屋屋主
  • 前后端分离架构设计(权限模型)

    前段时间分别用vue和react写了两个后台管理系统的模板vue-quasar-admin和3YAdmin。两个项目中都实现了基于RBAC的权限控制。因为本职工...

    掌上编程
  • NTP时间同步服务器在计算机网络重要性

    近几年来,随着计算机自动化系统水平的提高,在各大计算机监控系统、微机保护装置、微机故障录波装置以及各类数据管理机得到了广泛的应用,而这些自动装置的配合工作需要有...

    NTP网络同步时钟
  • 游戏文本关键词提取工作的尝试和探索

    如何将合适的游戏文本打上正确的关键词标签,并将内容推送给恰当的用户成为一个重要的课题。

    腾讯知文实验室
  • 新媒体必修课: 如何快速将gif图调整到300帧以内?

    ​为保证最终gif图的流畅,可以采用每隔N帧,抽取1帧的方式, 一张504帧的图片,我们需要每隔1帧抽取1帧微信一直以「克制」著称,为了给用户节省流量,微信公众...

    zhaoolee
  • 一个SQL语句引发的ORA-00600错误排查(一) (r9笔记第64天)

    最近有一个同事问我一个问题,说他运行一个SQL语句抛出了ORA-00600的错误,想让我帮忙分析一下,这种问题听了确实有兴趣,了解了问题的大体情 况之后,发现这...

    jeanron100
  • 生信(九)生信代码中的位操作

    我们知道,0和1构成的二进制充斥着计算机语言的世界。一般来说,我们对二进制可以操作的最小单位就是一个bit(位)了,一个bit要么是0,要么是1。在编写代码的过...

    一只羊

扫码关注云+社区

领取腾讯云代金券