前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >java高级进阶|定时器的分析

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

作者头像
码农王同学
发布2020-05-29 16:10:56
4930
发布2020-05-29 16:10:56
举报
文章被收录于专栏:后端Coder后端Coder

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

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

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

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

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

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

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

代码语言:javascript
复制
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秒就会打印当前线程名称和当前时间信息的定时器。

代码语言:javascript
复制
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);    }}

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

代码语言:javascript
复制
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秒的速率去触发定时器。

代码语言:javascript
复制
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仓库里面,所以这里想到它了就简单去写下吧,毕竟有文字的程序是符合自己目前文章风格的。

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

代码语言:javascript
复制
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();    }}

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

代码语言:javascript
复制
pool-1-thread-1Wed May 27 21:50:16 CST 2020

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

代码语言:javascript
复制
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);    }}

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

代码语言:javascript
复制
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

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

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

代码语言:javascript
复制
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创建线程的三种方式,这里就不做过多的说明了。

代码语言:javascript
复制
str = hello 定时器

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

代码语言:javascript
复制
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);    }}

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

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-05-28,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 码农王同学 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档