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;
@Slf4j
public 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 2020
Timer-0Wed May 27 21:14:30 CST 2020
Timer-0Wed May 27 21:14:31 CST 2020
Timer-0Wed May 27 21:14:32 CST 2020
Timer-0Wed May 27 21:14:33 CST 2020
Timer-0Wed May 27 21:14:34 CST 2020
Timer-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 2020
pool-1-thread-1Wed May 27 21:56:33 CST 2020
pool-1-thread-1Wed May 27 21:56:35 CST 2020
pool-1-thread-1Wed May 27 21:56:37 CST 2020
pool-1-thread-1Wed May 27 21:56:39 CST 2020

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

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

import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.*;
@Slf4j
public 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);
    }
}


这里想了解这个方法的可以自己运行下示例程序,日志信息就不输出了,这里也到了自己休息的时间了,不,自己玩的时间了,之所以会写这篇文章还不是心心念吗,不然自己不会及时输出这篇文章的,因为它在我的gitHub上面已经放了两年了,还是那样的输出方式,我喜欢的技术点都会在自己的心里至少要沉淀很久才会把它分享出来,好了,扯的有点远了,喜欢的点个在看呗。

我喜欢分享,你喜欢阅读@WwpwW

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值