ScheduledExecutorService 使用

ScheduledExecutorService 使用ScheduledExecutorService,我平时没有用过,他的最大优点除了线程池的特性以外,可以实现循环或延迟任务。ScheduledExecutorService,是基于线程池设计的定时任务类,每个调度任务都会分配到线程池中的一个线程去执行,也就是说,任务是并发执行,互不影响。需要注意,只有当调度任务来的时候,ScheduledExecutorService才会真正启动一个线程,其余时间ScheduledExecutorService都是出于轮询任务的状态。先来个简单的延迟任务调…

大家好,又见面了,我是你们的朋友全栈君。

ScheduledExecutorService,我平时没有用过,他的最大优点除了线程池的特性以外,可以实现循环或延迟任务。

ScheduledExecutorService,是基于线程池设计的定时任务类,每个调度任务都会分配到线程池中的一个线程去执行,也就是说,任务是并发执行,互不影响。

需要注意,只有当调度任务来的时候,ScheduledExecutorService才会真正启动一个线程,其余时间ScheduledExecutorService都是出于轮询任务的状态。

先来个简单的延迟任务调用,看看效果,重点在后面

ScheduledExecutorService本身也提供了只运行一次的延迟任务方法schedule,只在延迟时间后 运行一次

 private static ScheduledExecutorService scheduler;

    public static void main(String[] args) throws Exception {
        scheduler = Executors.newScheduledThreadPool(5);
        System.out.println("main thread time : " + formatDateToString(new Date()));
        // 循环任务,按照上一次任务的发起时间计算下一次任务的开始时间
        scheduler.schedule(((
                        new Runnable() {
                            @Override
                            public void run() {
                                System.out.println(
                                        " 开始 threadId = "
                                                + Thread.currentThread().getId()
                                                + ",,,threadName = " + Thread.currentThread().getName()
                                                + ",,,时间" +  formatDateToString(new Date())
                                );

                                try {
                                    Thread.sleep(1000);
                                    System.out.println(
                                            " 结束 threadId = "
                                                    + Thread.currentThread().getId()
                                                    + ",,,threadName = " + Thread.currentThread().getName()
                                                    + ",,,时间" + formatDateToString(new Date())
                                    );

                                } catch (InterruptedException e) {
                                    e.printStackTrace();
                                }
                            }
                        })),
                5,
                TimeUnit.SECONDS);
    }

    public static String formatDateToString(Date time) {
        SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
        return sdf.format(time);
    }

 只运行一次

ScheduledExecutorService 使用

 

划重点一:

cheduleAtFixedRate ,是以上一个任务开始的时间计时,120秒过去后,检测上一个任务是否执行完毕,如果上一个任务执行完毕,则当前任务立即执行,如果上一个任务没有执行完毕,则需要等上一个任务执行完毕后立即执行,之前我看到一些博客直接说的是按固定频率执行,但是不严谨,要注意如果你的执行任务的时间超过了间隔时间,那么就会变成一直连续循环执行,间隔时间的参数其实已经没发挥出作用了,其实一般我们也不会犯这样的错误,只是要清楚,固定频率是有条件的就行。

scheduleWithFixedDelay,是以上一个任务结束时开始计时,120秒过去后,立即执行。

划重点二:

关于定时线程池,好多人认为设置好频率(比如1Min),它会按照这个间隔按部就班的工作。但是,如果其中一次调度任务卡住的话,不仅这次调度失败,而且整个线程池也会停在这次调度上。 

源码中的注解也清楚的写着 If any execution of the task encounters an exception, subsequent executions are suppressed.

因此,注意在使用的时候涉及一些可能因为网络或者其他一些原因导致异常,要注意用try…catch…捕获

先写两个错误的示范和项目中注意的事项: 

错误一:cheduleAtFixedRate 这下面就是如果没有进行异常捕获,导致后续调度任务禁止 

private static ScheduledExecutorService scheduler;

    public static void main(String[] args) throws Exception {
        scheduler = Executors.newScheduledThreadPool(5);

        // 循环任务,按照上一次任务的发起时间计算下一次任务的开始时间
        scheduler.scheduleAtFixedRate(((
                        new Runnable() {
                            @Override
                            public void run() {
                                System.out.println(
                                        " 开始 threadId = "
                                                + Thread.currentThread().getId()
                                                + ",,,threadName = " + Thread.currentThread().getName()
                                                + ",,,时间" +  formatDateToString(new Date())
                                );

                                try {
                                    Thread.sleep(3000);

                                    System.out.println(
                                            " 结束 threadId = "
                                                    + Thread.currentThread().getId()
                                                    + ",,,threadName = " + Thread.currentThread().getName()
                                                    + ",,,时间" + formatDateToString(new Date())
                                    );

                                } catch (InterruptedException e) {
                                    e.printStackTrace();
                                }
                                //模拟抛出异常
                                if (1 == 1) {
                                    throw new RuntimeException("异常");
                                }
                            }
                        })),
                0, 1,
                TimeUnit.SECONDS);

        Thread.sleep(20000);
        System.out.println(
                " 主线程 threadId = "
                        + Thread.currentThread().getId()
                        + ",,,threadName = " + Thread.currentThread().getName()
                        + ",,,时间" + formatDateToString(new Date())
        );
    }

    public static String formatDateToString(Date time) {
        SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
        return sdf.format(time);
    }

ScheduledExecutorService 使用

错误二:scheduleAtFixedRate ,设置的period 间隔时间 小于了任务执行时间,导致任务连续执行

private static ScheduledExecutorService scheduler;

    public static void main(String[] args) throws Exception {
        scheduler = Executors.newScheduledThreadPool(5);

        // 循环任务,按照上一次任务的发起时间计算下一次任务的开始时间
        scheduler.scheduleAtFixedRate(((
                        new Runnable() {
                            @Override
                            public void run() {
                                System.out.println(
                                        " 开始 threadId = "
                                                + Thread.currentThread().getId()
                                                + ",,,threadName = " + Thread.currentThread().getName()
                                                + ",,,时间" +  formatDateToString(new Date())
                                );

                                try {
                                    Thread.sleep(3000);

                                    System.out.println(
                                            " 结束 threadId = "
                                                    + Thread.currentThread().getId()
                                                    + ",,,threadName = " + Thread.currentThread().getName()
                                                    + ",,,时间" + formatDateToString(new Date())
                                    );

                                } catch (InterruptedException e) {
                                    e.printStackTrace();
                                }
                            }
                        })),
                0, 1,
                TimeUnit.SECONDS);
    }

    public static String formatDateToString(Date time) {
        SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
        return sdf.format(time);
    }

可以看到其实间隔时间已经变成了 任务执行时间来控制,但是一般来说,很少有任务执行时间超过间隔时间,但是这个知识点还是要知道。 

ScheduledExecutorService 使用

 

scheduleAtFixedRate 正常使用 延迟时间1S,执行任务时间 1S,间隔时间3S,
private static ScheduledExecutorService scheduler;

    public static void main(String[] args) throws Exception {
        scheduler = Executors.newScheduledThreadPool(5);

        // 循环任务,按照上一次任务的发起时间计算下一次任务的开始时间
        scheduler.scheduleAtFixedRate(((
                        new Runnable() {
                            @Override
                            public void run() {
                                System.out.println(
                                        " 开始 threadId = "
                                                + Thread.currentThread().getId()
                                                + ",,,threadName = " + Thread.currentThread().getName()
                                                + ",,,时间" +  formatDateToString(new Date())
                                );

                                try {
                                    Thread.sleep(1000);

                                    System.out.println(
                                            " 结束 threadId = "
                                                    + Thread.currentThread().getId()
                                                    + ",,,threadName = " + Thread.currentThread().getName()
                                                    + ",,,时间" + formatDateToString(new Date())
                                    );

                                } catch (InterruptedException e) {
                                    e.printStackTrace();
                                }
                            }
                        })),
                0, 3,
                TimeUnit.SECONDS);
    }

    public static String formatDateToString(Date time) {
        SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
        return sdf.format(time);
    }

可以看到任务开始时间的间隔 为我们设置的 period 间隔时间3s,计算方式就是 initialDelay 初始延迟时间(第一次任务执行的间隔时间) +  n * period(间隔时间) 0s  3s  6s 来计算

ScheduledExecutorService 使用

scheduleWithFixedDelay 正常使用
private static ScheduledExecutorService scheduler;

    public static void main(String[] args) throws Exception {
        scheduler = Executors.newScheduledThreadPool(5);

        // 循环任务,按照上一次任务的发起时间计算下一次任务的开始时间
        scheduler.scheduleWithFixedDelay(((
                        new Runnable() {
                            @Override
                            public void run() {
                                System.out.println(
                                        " 开始 threadId = "
                                                + Thread.currentThread().getId()
                                                + ",,,threadName = " + Thread.currentThread().getName()
                                                + ",,,时间" +  formatDateToString(new Date())
                                );

                                try {
                                    Thread.sleep(3000);

                                    System.out.println(
                                            " 结束 threadId = "
                                                    + Thread.currentThread().getId()
                                                    + ",,,threadName = " + Thread.currentThread().getName()
                                                    + ",,,时间" + formatDateToString(new Date())
                                    );

                                } catch (InterruptedException e) {
                                    e.printStackTrace();
                                }
                            }
                        })),
                0, 1,
                TimeUnit.SECONDS);
    }

    public static String formatDateToString(Date time) {
        SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
        return sdf.format(time);
    }
scheduleWithFixedDelay以上一次任务的结束时间 + 延迟时间 = 下一次任务的开始时间。 

 

ScheduledExecutorService 使用

以上就是两个循环任务的使用

 

配合ThreadFactory 使用

ThreadFactory 一般用来管理线程的创建或者其他一些操作管理,这里我们重点是说下ScheduledExecutorService,关于ThreadFactory的用法和分析就不深入说了。

private static ScheduledExecutorService scheduler;

    public static void main(String[] args) throws Exception {
        scheduler = Executors.newScheduledThreadPool(5, new ThreadFactory() {
            private AtomicInteger counter = new AtomicInteger(0);

            //可以在这里对线程做一些操作
            @Override
            public Thread newThread(Runnable r) {
                int count = counter.incrementAndGet();
                System.out.println("线程创建counter = " + count);
                Thread thread = new Thread(r);
                thread.setName("测试线程"+count);
                return thread;
            }
        });
        System.out.println("main thread time : " + formatDateToString(new Date()));
        // 循环任务,按照上一次任务的发起时间计算下一次任务的开始时间
        scheduler.scheduleAtFixedRate(((
                        new Runnable() {
                            @Override
                            public void run() {
                                System.out.println(
                                        " 开始 threadId = "
                                                + Thread.currentThread().getId()
                                                + ",,,threadName = " + Thread.currentThread().getName()
                                                + ",,,时间" +  formatDateToString(new Date())
                                );
                            }
                        })),
                1,5,
                TimeUnit.SECONDS);
    }

    public static String formatDateToString(Date time) {
        SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
        return sdf.format(time);
    }

 其实就是提供了一个扩展点,允许你在创建线程的时候,做一些管理或者说一些其他的操作

ScheduledExecutorService 使用

也可以使用 ThreadFactoryBuilder 构建类 来构建FactoryBuilder

scheduler = Executors.newScheduledThreadPool(5,new ThreadFactoryBuilder()
                .setNameFormat("测试线程-%d")
//                .setDaemon(true) //这个参数是设置为守护线程 也叫 服务线程
                .build());

定义:守护线程–也称“服务线程”,在没有用户线程可服务时会自动离开。优先级:守护线程的优先级比较低,用于为系统中的其它对象和线程提供服务。
设置:通过setDaemon(true)来设置线程为“守护线程”;将一个用户线程设置为守护线程的方式是在 线程对象创建 之前 用线程对象的setDaemon方法。用来设置线程

生命周期:守护进程(Daemon)是运行在后台的一种特殊进程。它独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。也就是说守护线程不依赖于终端,但是依赖于系统,与系统“同生共死”。那Java的守护线程是什么样子的呢。当JVM中所有的线程都是守护线程的时候,JVM就可以退出了;如果还有一个或以上的非守护线程则JVM不会退出。

比如: 垃圾回收线程就是一个经典的守护线程,当我们的程序中不再有任何运行的Thread,程序就不会再产生垃圾,垃圾回收器也就无事可做,所以当垃圾回收线程是JVM上仅剩的线程时,垃圾回收线程会自动离开。它始终在低级别的状态中运行,用于实时监控和管理系统中的可回收资源。

当java虚拟机中没有非守护线程在运行的时候,java虚拟机会关闭。当所有常规线程运行完毕以后,守护线程不管运行到哪里,虚拟机都会退出运行。所以你的守护线程最好不要写一些会影响程序的业务逻辑。否则无法预料程序到底 会出现什么问题。

 

 

 

 

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/137141.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 依赖关系(“使用”关系)

    依赖关系(“使用”关系)依赖关系,可以理解成“USE-A”关系即使用关系。依赖关系是一种使用关系,如果A类中的某个方法使用了B类对象,那么就可以说A类依赖B类。A类方法的形式参数是B类类型。也就是说A类对象如果要使用方法

    2022年7月3日
    34
  • Win10+Ubuntu双系统修复Ubuntu系统引导

    Win10+Ubuntu双系统修复Ubuntu系统引导amp amp NBSP amp amp NBSP amp amp NBSP amp amp NBSP 这两天笔者安装 win10 ubuntu16 04 双系统 因为网络上能找到大量的资料 安装过程此处就不多讲 因为笔者电脑是华硕主板 bios 默认设置为安全启动 笔者猜测会阻止加载 ubuntu 引导 导致双系统不能随意引导 先不管那么多 现在的问题是 Ubuntu 已经安装成功 开机直接进入 win10 所以笔者的

    2025年11月23日
    5
  • cosx等价无穷小_第一章 函数与极限 第七节 无穷小的比较[通俗易懂]

    cosx等价无穷小_第一章 函数与极限 第七节 无穷小的比较[通俗易懂]我的公众号“每日晴天”,可关注领取我的笔记pdf版哦~——————————————————————————本文主要首先把书上的定义和知识点总结起来,方便复习要点背诵,同时在最后分割线之后补充自己当时在学习定义的过程中的理解!欢迎评论自己不懂的内容,我后续补充理解哦~谢谢支持(#^.^#)学理科其…

    2022年8月11日
    4
  • 『数据可视化』基于Python的数据可视化工具「建议收藏」

    『数据可视化』基于Python的数据可视化工具「建议收藏」如何做Python的数据可视化?pyecharts是一个用于生成Echarts图表的类库。Echarts是百度开源的一个数据可视化JS库。主要用于数据可视化。一、安装pyecharts兼容Python2和Python3。目前版本为0.1.4pipinstallpyecharts二、入门首先开始来绘制你的第一个图表

    2022年6月22日
    36
  • navicat连接sqlserver数据库Windows验证_pb连接mysql数据库

    navicat连接sqlserver数据库Windows验证_pb连接mysql数据库连接配置方式如图:有时候Navicat并没有初始化安装sqlncli,所以连接的时候会报没有默认驱动,如图:解决方法:在navicat目录下找到对应驱动,点击安装即可,安装过程中设置为全部允许安装…

    2022年4月19日
    156
  • 倒排索引

    倒排索引

    2021年10月23日
    53

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号