不灭的焱

革命尚未成功,同志仍须努力下载JDK17

作者:Albert.Wen  添加时间:2022-10-06 20:25:42  修改时间:2024-04-13 02:08:03  分类:Java基础  编辑

案例1:定时器打印Hello World!

import java.text.ParseException;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;

/**
 * @author :jiaolian
 * @date :Created in 2021-01-05 20:42
 * @description:Timer启动后内置线程不销毁
 * @modified By:
 * 公众号:叫练
 */
public class TimerThreadNoStopTest {

    //TimerTask为抽象类,继承TimerTask类必须要实现里面抽象方法
    private static class Task extends TimerTask {
        @Override
        public void run() {
            System.out.println("hello world!");
        }
    }

    public static void main(String[] args) throws ParseException {

        Timer timer = new Timer();
        Task task = new Task();
        long currenTime = System.currentTimeMillis();
        //提交Task线程;程序按传入日期运行
        timer.schedule(task,new Date(currenTime));
      
        //【注意】用完定时器后,要及时取消它
        //timer.cancel();
        //timer.purge();
    }
}

如上面程序代码,Timer提交了一个task任务并传入了currenTime当前时间,控制台马上打印了"hello world!",如果schedule传入的第二个参数是new Date(currenTime+2000)表示延迟2m执行task任务,这里简单使用方法就不过多的描述了。下图是程序控制台打印结果,如果大家执行了你会发现一个问题,程序一直不结束运行,也就是程序不死。那是什么导致这样的结果呢?

线程不死的原因?

原因分析:如下图所示,主线程执行Timer timer = new Timer();会创建了一个新的子线程timer,timer线程通过死循环来取队列里面的任务task[1],队列其实就是一个数组实现TaskQueue,队列里面如果没有任务,那timer线程就会一直等待直到主线程调用schedule提交任务,主线程就会将task加入到TaskQueue队列数组并通知timer线程执行任务并删除队列的第一个任务,如果是主线程提交的是定时任务,就会将任务重新加入队列,任务执行完毕后,如果此时队列为空,timer线程就会继续等待任务提交到队列,一直会循环上面的过程。如果想退出timer线程,可以调用cancel方法会退出死循环。线程不死原因是timer线程一直在等待主线程提交任务,timer线程和主线程通信是通过调用wait/notify实现。我们之前做生产者/消费者案例时详细介绍过,这里老铁不再重复叙述了,可以去翻看文章**《母鸡下蛋实例》**。这个过程中,我们发现timer是一个单线程,我是单线程怎么了?单线程也有错吗?思考个问题,如果timer这个单线程提交了两个任务怎么办?我们看下面代码!

案例2:单线程问题

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;

/**
 * @author :jiaolian
 * @date :Created in 2021-01-06 10:53
 * @description:多任务执行测试,任务只能顺序执行;
 * @modified By:
 * 公众号:叫练
 */
public class MultTaskExecuteTest {

    private static final SimpleDateFormat SIMPLE_DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    private static class  MyTask1 extends TimerTask {

        @Override
        public void run() {
            System.out.println("task1 begin:"+SIMPLE_DATE_FORMAT.format(new Date()));
            try {
                Thread.sleep(10000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("task1 end:"+SIMPLE_DATE_FORMAT.format(new Date()));
        }
    }

    private static class  MyTask2 extends TimerTask {

        @Override
        public void run() {
            System.out.println("task2 begin:"+SIMPLE_DATE_FORMAT.format(new Date()));
            System.out.println("task2 end:"+SIMPLE_DATE_FORMAT.format(new Date()));
        }
    }


    public static void main(String[] args){
        Timer timer = new Timer();
        MyTask1 myTask1 = new MyTask1();
        MyTask2 myTask2 = new MyTask2();
        long curTime = System.currentTimeMillis();
        System.out.println("当前时间:"+SIMPLE_DATE_FORMAT.format(curTime));
        timer.schedule(myTask1,new Date(curTime));
        //myTask1执行时间过长,myTask2 被执行时间会被延迟;
        timer.schedule(myTask2,new Date(curTime+1000));
    }

}

如上面程序代码,timer线程提交了两个任务myTask1,myTask2,myTask1任务会立刻执行,myTask2计划延迟一秒执行,myTask1执行过程中会休息10秒钟,我们观察任务执行时间如下图所示,myTask2任务是等待myTask1任务执行完毕后再执行的,其实myTask2只是延迟一秒执行,结果却延迟了10秒,说明了timer单线程会串行化任务导致myTask2延迟执行,所以**Timer是适合轻量级定时任务,如果设置大量任务,可能会存在延迟执行情况。

定时器实际应用场景

在日常系统开发中,相信你遇到过类似需要重复执行的任务,比如每天凌晨2点清理数据库某张表的垃圾数据,页面显示设备(服务器)运行状态也需要每隔3秒调用设备状态接口查询设备情况等,这些功能开发都需要用到定时器(Timer),当然Timer定时器也有自身的缺陷,比如它是单线程的,后面会说到线程池中的定时器是多线程的,可以优化Timer,所以掌握Timer定时器为后面学习高阶内容打好基础。知其然知其所以然,在实际应用中我们能得心应手!

 

 

摘自:https://zhuanlan.zhihu.com/p/342886230