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虚拟机会关闭。当所有常规线程运行完毕以后,守护线程不管运行到哪里,虚拟机都会退出运行。所以你的守护线程最好不要写一些会影响程序的业务逻辑。否则无法预料程序到底 会出现什么问题。

 

 

 

 

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

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

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)
blank

相关推荐

  • C# 读写 Photoshop PSD文件 操作类「建议收藏」

    C# 读写 Photoshop PSD文件 操作类「建议收藏」使用方法显示PSDOpenFileDialog_Dialog=newOpenFileDialog();_Dialog.Filter=”*.psd|*.psd”;if(_Dialog.ShowDialog()==DialogResult.OK){Zgke.MyImage.Image…

  • Python学习笔记_05:使用Flask+MySQL实现用户登陆注册以及增删查改操作「建议收藏」

    Python学习笔记_05:使用Flask+MySQL实现用户登陆注册以及增删查改操作

  • tar 解压缩命令详解

    tar 解压缩命令详解以下是对tar命令的一些总结:解压操作:tar解压缩命令详解这五个是独立的命令,压缩解压都要用到其中一个,可以和别的命令连用但只能用其中一个。下面的参数是根据需要在压缩或解压档案时可选的。下

  • 简单页面+java后台+数据库,实现从页面对数据库的增删改查

    简单页面+java后台+数据库,实现从页面对数据库的增删改查在实现简单网页上对数据内容进行增删改查,需要用到三个部分,分别是jsp网页部分+java后台部分+数据库表我用一个新闻的例子来实现,首先编写java后台程序java后台程序:我们用三层的模式进行设计:分别是servlet,service,dao层,并且建立个实体包用来打包数据库和后台要用到的属性截个图首先是写功能写的顺序分别是从servlet,service,dao层…

  • mysql窗口函数over中rows_MySQL窗口函数

    mysql窗口函数over中rows_MySQL窗口函数首先在讲解OVER函数之前,我们先来看一下这一片文章:https://www.jianshu.com/p/e5c5bfb1e28b

  • 大数据数仓建模

    大数据数仓建模        大数据项目之电商数仓(用户行为数据采集)数据仓库简介      1.什么是数据库?    数据库(database)是按照数据结构来组织,存储和管理数据的建立在计算机存储设备上的仓库。    数据库是长期存储在计算机内,有组织的,可共享的数据集合。数据库中的数据指的是以一定的数据模型组织,描述和存储在一起,具有尽可能小的冗余度,较高的数据独立性和易扩展性的特点并可在一定范围内为多个用户共享。    常用的数据库有mysql,oracle,sqlserver等。作用不一样,数据库是

发表回复

您的电子邮箱地址不会被公开。

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