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)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • Spatial Transformer Networks(STN)理解

    Spatial Transformer Networks(STN)理解文章目录STN的作用STN的基本结构前向过程Tensorflow部分实现代码实验结果DistortedMNISTGermanTrafficSignRecognitionBenchmark(GTSRB)datasetSTN的作用之前参加过一个点云数据分类的比赛,主要借鉴了PointNet的网络结构,在PointNet中使用到了两次STN。点云数据存在两个主要问题:1、无序性:点云本…

    2022年10月19日
    0
  • MinGW MinGW-w64 TDM-GCC等工具链之间的差别与联系「建议收藏」

    MinGW MinGW-w64 TDM-GCC等工具链之间的差别与联系

    2022年1月22日
    104
  • JAVA实验室设备管理系统代码_java做一个简单学生管理系统

    JAVA实验室设备管理系统代码_java做一个简单学生管理系统本系统主要采用面向对象的基于B/S架构的设计,开发平台采用myeclipse,程序设计语言采用java语言,并使用三层架构,采用mysql作为后台数据库。实验室设备管理系统基本实现用户与管理员之间的交互,用户查询设备并对自己需要的进行借用,管理员对用户的设备借用进行审核,最后对于设备的维修进行记录。其基本步骤有以下几点:1.需求分析:以本学院为例询问老师和同学得出系统需求,查找并研究资料,熟悉开发…

    2022年10月13日
    0
  • metasploit指令_msfconsole下载

    metasploit指令_msfconsole下载在MSF里面msfconsole可以说是最流行的一个接口程序。很多人一开始碰到msfconsole的时候就害怕了。那么多复杂的命令语句需要学习,但是msfconsole真的是一个强大的接口程序。Msfconsole提供了一个一体化的集中控制台。通过msfconsole,你可以访问和使用所有的metasploit的插件,payload,利用模块,post模块等等。Msfconsole还有第三方程序的…

    2022年9月7日
    0
  • Nacos 2.0_一个数的0倍是多少

    Nacos 2.0_一个数的0倍是多少点击关注公众号,Java干货及时送达3月20号,Nacos2.0.0正式发布了!Nacos简介:“一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。通俗点讲,Nacos…

    2022年9月20日
    1
  • zabbix 监控多个mysql_zabbix 监控多实例mysql[通俗易懂]

    zabbix 监控多个mysql_zabbix 监控多实例mysql[通俗易懂]zabbix监控多实例mysql一台服务器上开启了3个mysql实例进程,占用不同的端口3306、3307、3308原理说明:通过自动发现规则来获取MySQL实例的端口,自动发现规则上的{$MYSQLPORT}是要传递给agent自动发现脚本的参数,这个值是从主机定义的宏{$MYSQLPORT}获取过来的,自动发现的脚本将其解析成{#MYSQLPORT}:端口的形式,监控项原型再根据{#MYS…

    2022年5月1日
    60

发表回复

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

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