ScheduledExecutorService使用介绍

JUC包(java.util.concurrent)中提供了对定时任务的支持,即ScheduledExecutorService接口。

本文对ScheduledExecutorService的介绍,将基于Timer类使用介绍进行,因此请先阅读Timer类使用介绍文章。

1. Timer使用介绍

一、创建ScheduledExecutorService对象

ScheduledExecutorService executorService = Executors.newScheduledThreadPool(5);

二、ScheduledExecutorService方法

ScheduledExecutorService实现了ExecutorService接口,ExecutorService接口中的方法事实上属于线程池相关的一般方法,不在本文讨论。

ScheduledExecutorService本身提供了以下4个方法:

  • ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit):延迟delay单位时间后,执行一次任务

  • ScheduledFuture schedule(Callable callable, long delay, TimeUnit unit):延迟delay单位时间后,执行一次任务

  • ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit):延迟initialDelay单位时间后,执行一次任务,之后每隔period单位时间执行一次任务(固定速率)

  • ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit):延迟initialDelay单位时间后,执行一次任务,之后每隔period单位时间执行一次任务(固定延时)

ScheduledExecutorService和Timer进行对比,两者所提供的方法是类似的,区别在于Timer有提供指定时间点执行任务,而ScheduledExecutorService没有提供。
Timer提供的方法返回值均为void,而ScheduledExecutorService的方法返回值均为ScheduledFuture(继承于Future接口)。

三、固定速率和固定延时的区别

和Timer一样,我们用示例来展示ScheduledExecutorService固定速率和固定延时的区别,并与Timer进行对比。

1. 固定速率

示例:

System.out.println("启动于:" + DateUtil.formatNow());
ScheduledExecutorService executorService = Executors.newScheduledThreadPool(5);
executorService.scheduleAtFixedRate(
        new Runnable() {
            int i = 1;
            @Override
            public void run() {
                System.out.print(i + " " + DateUtil.formatNow() + " 开始执行, ");
                if(i == 3) {
                    ThreadUtil.sleep(11 * 1000);
                }
                System.out.println(DateUtil.formatNow() + " 结束");
                i ++;
            }
        },
5, 2, TimeUnit.SECONDS);

输出:

启动于:2022-10-31 17:15:44
1 2022-10-31 17:15:49 开始执行, 2022-10-31 17:15:49 结束
2 2022-10-31 17:15:51 开始执行, 2022-10-31 17:15:51 结束
3 2022-10-31 17:15:53 开始执行, 2022-10-31 17:16:04 结束 *
4 2022-10-31 17:16:04 开始执行, 2022-10-31 17:16:04 结束 *
5 2022-10-31 17:16:04 开始执行, 2022-10-31 17:16:04 结束 *
6 2022-10-31 17:16:04 开始执行, 2022-10-31 17:16:04 结束 *
7 2022-10-31 17:16:04 开始执行, 2022-10-31 17:16:04 结束 *
8 2022-10-31 17:16:04 开始执行, 2022-10-31 17:16:04 结束 *
9 2022-10-31 17:16:05 开始执行, 2022-10-31 17:16:05 结束
10 2022-10-31 17:16:07 开始执行, 2022-10-31 17:16:07 结束
11 2022-10-31 17:16:09 开始执行, 2022-10-31 17:16:09 结束

没有11秒耗时的情况下,正常应该是输出:

启动于:2022-10-31 17:15:44
1 2022-10-31 17:15:49 开始执行, 2022-10-31 17:15:49 结束
2 2022-10-31 17:15:51 开始执行, 2022-10-31 17:15:51 结束
3 2022-10-31 17:15:53 开始执行, 2022-10-31 17:15:53 结束
4 2022-10-31 17:15:55 开始执行, 2022-10-31 17:15:55 结束
5 2022-10-31 17:15:57 开始执行, 2022-10-31 17:15:57 结束
6 2022-10-31 17:15:59 开始执行, 2022-10-31 17:15:59 结束
7 2022-10-31 17:16:01 开始执行, 2022-10-31 17:16:01 结束
8 2022-10-31 17:16:03 开始执行, 2022-10-31 17:16:03 结束
9 2022-10-31 17:16:05 开始执行, 2022-10-31 17:16:05 结束
10 2022-10-31 17:16:07 开始执行, 2022-10-31 17:16:07 结束
11 2022-10-31 17:16:09 开始执行, 2022-10-31 17:16:09 结束

从测试结果中可以看出,当有一次任务执行耗时过长,超出了设定的period时间单位,将会影响后续5次任务准时执行,当耗时任务完成后,ScheduledExecutorService将会立即将延误的5次任务一起补上,并保障后续的任务按预期的时间点执行。
这与ScheduledExecutorService固定速率的效果与Timer是完全一样的,读者可直接参考Timer的固定速率介绍。

2. 固定延时

示例:

System.out.println("启动于:" + DateUtil.formatNow());
ScheduledExecutorService executorService = Executors.newScheduledThreadPool(5);
executorService.scheduleWithFixedDelay(
        new Runnable() {
            int i = 1;
            @Override
            public void run() {
                System.out.print(i + " " + DateUtil.formatNow() + " 开始执行, ");
                if(i == 3) {
                    ThreadUtil.sleep(11 * 1000);
                }
                System.out.println(DateUtil.formatNow() + " 结束");
                i ++;
            }
        },
5, 2, TimeUnit.SECONDS);

输出:

1 2022-10-31 17:16:41 开始执行, 2022-10-31 17:16:41 结束
2 2022-10-31 17:16:43 开始执行, 2022-10-31 17:16:43 结束
3 2022-10-31 17:16:45 开始执行, 2022-10-31 17:16:56 结束 *
4 2022-10-31 17:16:58 开始执行, 2022-10-31 17:16:58 结束
5 2022-10-31 17:17:00 开始执行, 2022-10-31 17:17:00 结束
6 2022-10-31 17:17:02 开始执行, 2022-10-31 17:17:02 结束
7 2022-10-31 17:17:04 开始执行, 2022-10-31 17:17:04 结束
8 2022-10-31 17:17:06 开始执行, 2022-10-31 17:17:06 结束
9 2022-10-31 17:17:08 开始执行, 2022-10-31 17:17:08 结束

没有11秒耗时的情况下,正常应该是输出:

1 2022-10-31 17:16:41 开始执行, 2022-10-31 17:16:41 结束
2 2022-10-31 17:16:43 开始执行, 2022-10-31 17:16:43 结束
3 2022-10-31 17:16:45 开始执行, 2022-10-31 17:16:45 结束
4 2022-10-31 17:16:47 开始执行, 2022-10-31 17:16:47 结束
5 2022-10-31 17:16:49 开始执行, 2022-10-31 17:16:49 结束
6 2022-10-31 17:16:51 开始执行, 2022-10-31 17:16:51 结束
7 2022-10-31 17:16:53 开始执行, 2022-10-31 17:16:53 结束
8 2022-10-31 17:16:55 开始执行, 2022-10-31 17:16:55 结束
9 2022-10-31 17:16:57 开始执行, 2022-10-31 17:16:57 结束

固定延时是当任务执行耗时过长,超出设定的delay时间单位,后续的任务将会被顺延推迟,这个设计是与Timer一样的,但与Timer却有一点小区别。
在Timer类使用介绍中,曾提到Timer类固定延时下与我想象的不太一致,Timer在第3次任务执行完成后会立即执行第4次任务,接着才是间隔2秒执行第5次任务。
而ScheduledExecutorService则与我的想象完全一致,当第3次任务执行完成后,会间隔2秒再执行第4次任务。
所以固定延时下,Timer和ScheduledExecutorService的实现是有一点区别的。

四、调度多个任务

在Timer中,一个TimerTask对象是一个任务。
而在ScheduledExecutorService中,则一个Runnable对象一个任务。
第三节介绍的是固定速率和固定延时是如何影响一个可重复执行任务(一个Runnable对象)的多次执行的。
而本节介绍的是ScheduledExecutorService如何同时调度多个可重复执行任务的。

与Timer内部仅1个线程不同,ScheduledExecutorService内部采用的是线程池,是支持自己设定线程数的。
那么理论上来说,如果要加入2个任务,ScheduledExecutorService设定线程数为2,就不会出现相互影响的情况。

我们来验证一下。

定义任务,当执行第3次时将会休眠11秒:

class Task implements Runnable {

    private int i = 1;

    private String name;

    public Task(String name) {
        this.name = name;
    }

    @Override
    public void run() {
        System.out.println(i + " " + name + ":" + DateUtil.formatNow() + " 开始执行");
        if(i == 3) {
            ThreadUtil.sleep(11 * 1000);
        }
        System.out.println(i + " " + name + ":" + DateUtil.formatNow() + " 执行结束");
        i ++;
    }
}

使用ScheduledExecutorService进行调度:

System.out.println("启动于:" + DateUtil.formatNow());
ScheduledExecutorService executorService = Executors.newScheduledThreadPool(2);

Task task1 = new Task("task1");
Task task2 = new Task("task2");

executorService.scheduleWithFixedDelay(task1, 5, 2, TimeUnit.SECONDS);
executorService.scheduleWithFixedDelay(task2, 5, 2, TimeUnit.SECONDS);

由于控制台输出时,task1和task2的日志会混在一起,不容易阅读,我这边将task1和task2的日志分开。
task1日志:

启动于:2022-10-31 17:49:51
1 task1:2022-10-31 17:49:56 开始执行
1 task1:2022-10-31 17:49:56 执行结束
2 task1:2022-10-31 17:49:58 开始执行
2 task1:2022-10-31 17:49:58 执行结束
3 task1:2022-10-31 17:50:00 开始执行
3 task1:2022-10-31 17:50:11 执行结束
4 task1:2022-10-31 17:50:13 开始执行
4 task1:2022-10-31 17:50:13 执行结束
5 task1:2022-10-31 17:50:15 开始执行
5 task1:2022-10-31 17:50:15 执行结束

task2日志:

启动于:2022-10-31 17:49:51
1 task2:2022-10-31 17:49:56 开始执行
1 task2:2022-10-31 17:49:56 执行结束
2 task2:2022-10-31 17:49:58 开始执行
2 task2:2022-10-31 17:49:58 执行结束
3 task2:2022-10-31 17:50:00 开始执行
3 task2:2022-10-31 17:50:11 执行结束
4 task2:2022-10-31 17:50:13 开始执行
4 task2:2022-10-31 17:50:13 执行结束
5 task2:2022-10-31 17:50:15 开始执行

经过测试可以确定,当加入的任务数不超过线程池线程数时,即使任务存在耗时也不会相互影响,而仅是影响自身任务下一次执行的时间点。

那如果加入任务数超出了线程数呢?
我们测试一下加入3个任务,线程数仍然为2.

Task task1 = new Task("task1");
Task task2 = new Task("task2");
Task task3 = new Task("task3");

executorService.scheduleWithFixedDelay(task1, 5, 2, TimeUnit.SECONDS);
executorService.scheduleWithFixedDelay(task2, 5, 2, TimeUnit.SECONDS);
executorService.scheduleWithFixedDelay(task3, 5, 2, TimeUnit.SECONDS);

将三个任务的日志分开展示。
task1:

启动于:2022-10-31 17:53:22
1 task1:2022-10-31 17:53:27 开始执行
1 task1:2022-10-31 17:53:27 执行结束
2 task1:2022-10-31 17:53:29 开始执行
2 task1:2022-10-31 17:53:29 执行结束
3 task1:2022-10-31 17:53:31 开始执行
3 task1:2022-10-31 17:53:42 执行结束
4 task1:2022-10-31 17:53:44 开始执行
4 task1:2022-10-31 17:53:44 执行结束
5 task1:2022-10-31 17:53:46 开始执行
5 task1:2022-10-31 17:53:46 执行结束
6 task1:2022-10-31 17:53:48 开始执行
6 task1:2022-10-31 17:53:48 执行结束
7 task1:2022-10-31 17:53:50 开始执行
7 task1:2022-10-31 17:53:50 执行结束
8 task1:2022-10-31 17:53:52 开始执行
8 task1:2022-10-31 17:53:52 执行结束
9 task1:2022-10-31 17:53:54 开始执行
9 task1:2022-10-31 17:53:54 执行结束
10 task1:2022-10-31 17:53:56 开始执行
10 task1:2022-10-31 17:53:56 执行结束

task2:

启动于:2022-10-31 17:53:22
1 task2:2022-10-31 17:53:27 开始执行
1 task2:2022-10-31 17:53:27 执行结束
2 task2:2022-10-31 17:53:29 开始执行
2 task2:2022-10-31 17:53:29 执行结束
3 task2:2022-10-31 17:53:31 开始执行
3 task2:2022-10-31 17:53:42 执行结束
4 task2:2022-10-31 17:53:44 开始执行
4 task2:2022-10-31 17:53:44 执行结束
5 task2:2022-10-31 17:53:46 开始执行
5 task2:2022-10-31 17:53:46 执行结束
6 task2:2022-10-31 17:53:48 开始执行
6 task2:2022-10-31 17:53:48 执行结束
7 task2:2022-10-31 17:53:50 开始执行
7 task2:2022-10-31 17:53:50 执行结束
8 task2:2022-10-31 17:53:52 开始执行
8 task2:2022-10-31 17:53:52 执行结束
9 task2:2022-10-31 17:53:54 开始执行
9 task2:2022-10-31 17:53:54 执行结束
10 task2:2022-10-31 17:53:56 开始执行
10 task2:2022-10-31 17:53:56 执行结束

task3:

启动于:2022-10-31 17:53:22
1 task3:2022-10-31 17:53:27 开始执行
1 task3:2022-10-31 17:53:27 执行结束
2 task3:2022-10-31 17:53:29 开始执行
2 task3:2022-10-31 17:53:29 执行结束
3 task3:2022-10-31 17:53:42 开始执行
3 task3:2022-10-31 17:53:53 执行结束
4 task3:2022-10-31 17:53:55 开始执行
4 task3:2022-10-31 17:53:55 执行结束
5 task3:2022-10-31 17:53:57 开始执行
5 task3:2022-10-31 17:53:57 执行结束

从以上日志可以看出,task1和task2执行是正常的,但是task3从第3次执行开始出现错误。
task3第三次时间点正确时间应该是17:53:31,而实际上被推迟到了17:53:42才开始。
从这点我们可以推测出,当时2个线程都在执行task1、task2的耗时11秒的第3次任务,导致task3被推迟。
因此,我们在使用ScheduledExecutorService调度多个任务时,应注意尽可能缩短任务的处理耗时,以及避免任务数超出线程数。

五、其他要点

任务执行过程中抛出异常会发生什么情况?

Timer内部是单个线程处理所有任务,当抛出异常时,Timer线程将终止运行;
ScheduledExecutorService内部是一个线程池,当抛出异常时,此任务所在线程将会终止运行被回收,该任务后续无法再触发执行,其他线程不受影响,因此编写任务执行代码要注意捕获异常。

  • 5
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java SE实践教程 源代码 2010-9-13 声明:在网上找了好久才找到这个源码,真不容易啊,请大家多多支持。 2010-9-13 内容简介:此书结合具体实例讲解,通俗易懂,又不乏深度。我觉得这本书写的确实不错,堪称经典,市面上这样的书实在太少了,所以在这里发布下,供大家共享。本书从编程技术、项目实践以及软件工程的角度出发,如果大家想学习基础语法部分,建立去下载别的书籍。当然这本书也讲解了语法,是从实战的角度讲解的。 目录回到顶部↑第1章 进驻爪哇岛——JAVA的基本语法. 1 1.1 讲解 2 1.1.1 爪哇岛的历史与演变 2 1.1.2 爪哇岛基本生存规则 4 1.1.3 爪哇岛上新人新风尚 11 1.2 练习 15 1.2.1 搭建Java开发环境 15 1.2.2 体验Java程序开发 21 1.2.3 J2SE 5.0新特性实践 26 1.3 小结 35 第2章 对象无处不在——面向对象的基本概念 37 2.1 讲解 38 2.1.1 什么是面向对象 38 2.1.2 面向对象的基本概念 38 2.1.3 Java对面向对象的支持 41 2.2 练习 42 2.2.1 JavaBeans技术开发可重用组件 42 2.2.2 面向对象的基础实践 44 2.3 小结 51 第3章 当一个变成多个——集合框架的基本概念 53 .3.1 讲解 54 3.1.1 集合概述 54 3.1.2 Collection接口 54 3.1.3 泛型(Generics) 56 3.1.4 Map接口 57 3.2 练习 59 3.2.1 创建课程管理系统 59 3.3 小结 68 第4章 数据传送的管道——JAVA I/O 71 4.1 讲解 72 4.1.1 流——Java I/O的基础 72 4.1.2 Java I/O库 72 4.2 练习 74 4.2.1 数据传送的通道 74 4.2.2 管道的一端 76 4.2.3 文件处理 78 4.2.4 基于对象的读写 80 4.2.5 NIO 85 4.3 小结 89 第5章 如何走得更稳——测试驱动的基本概念 91 5.1 讲解 92 5.1.1 什么是JUnit 92 5.1.2 使用JUnit的一般过程 92 5.1.3 安装JUnit 93 5.2 编写单元测试 93 5.2.1 第1个单元测试 93 5.3 编写单元测试的步骤 95 5.3.1 常用断言 95 5.3.2 TestSuite 96 5.3.3 JUnit框架组成 96 5.4 练习 97 5.4.1 创建JUnit单元测试 97 5.4.2 setUp和tearDown 102 5.4.3 使用TestSuite 103 5.5 补充:JUNIT 4的新增特性 104 5.5.1 测试方法 104 5.5.2 初始化方法 105 5.5.3 TestSuite初始化 106 5.5.4 兼容性 106 5.6 小结 107 第6章 三头六臂——线程和同步的基本概念 109 6.1 讲解 110 6.1.1 什么是线程 110 6.1.2 创建线程 110 6.1.3 线程的生命周期 112 6.1.4 线程的优先级 114 6.1.5 中断线程 115 6.1.6 线程组 116 6.1.7 处理未被捕获的异常 117 6.1.8 守护线程 117 6.2 同步与锁 118 6.2.1 synchronized和同步 118 6.2.2 锁对象 120 6.2.3 Condition对象 121 6.2.4 再谈synchronized 122 6.3 协调任务 124 6.3.1 线程池和Executor 124 6.3.2 Callable和Future 126 6.3.3 ScheduledExecutorService 127 6.4 线程安全的集合和同步器 128 6.4.1 阻塞队列 128 6.4.2 指定阻塞时间 130 6.4.3 同步器 131 6.4.4 Atomic类型 134 6.5 练习 134 6.5.1 线程间同步 134 6.5.2 生产者、消费者问题.. 137 6.6 小结 140 第7章 我要彩色照片——SWING的基本概念 141 7.1 讲解 142 7.1.1 Swing的基本概念 142 7.1.2 Swing组件继承关系 142 7.1.3 Swing组件一览 143 7.1.4 Swing和MVC设计模式 144 7.1.5 Swing的单线程模型 145 7.2 练习 148 7.2.1 第1个Swing程序 148 7.2.2 外观感觉 150 7.2.3 事件侦听器 151 7.2.4 Swing基本控件和窗口 155 7.2.5 Swing容器 176 7.2.6 Swing高级控件 181 7.3 小结 187 第8章 朋友们,你们在哪里——JAVA数据库运用 189 8.1 讲解 190 8.1.1 数据库的基本概念 190 8.1.2 了解Java DB 190 8.1.3 JDBC 与 JDBC 4.0 191 8.1.4 用Java让数据库动起来 192 8.1.5 事务处理简介 194 8.2 练习 195 8.2.1 数据库操作 195 8.2.2 我的联系手册 199 8.2.3 事务处理 221 8.3 小结 222 第9章 还想再见到你——数据持久化 223 9.1 讲解 224 9.1.1 数据持久化的基本概念 224 9.1.2 数据持久化技术简介 224 9.1.3 Java中的对象关系映射 225 9.1.4 对象XML序列化 227 9.2 练习 228 9.2.1 我的联系手册(JPA实现) 228 9.2.2 我的联系手册(JAXB实现) 238 9.3 小结 242 第10章 准备环球旅行——应用程序国际化 243 10.1 讲解 244 10.1.1 概念介绍 244 10.1.2 设置Locale 249 10.1.3 隔离语言环境相关数据 252 10.1.4 格式化 258 10.2 练习 266 10.2.1 对单独的文件进行国际化 266 10.2.2 在设计时国际化GUI表单 273 10.2.3 国际化整个项目 277 10.3 小结 281 第11章 请保持联系——JAVA网络连接 283 11.1 讲解 284 11.1.1 Java网络连接的基本概念 284 11.1.2 使用TCP协议的Socket网络编程 285 11.1.3 使用UDP协议的Socket 网络编程 289 11.1.4 多点传送和MulticastSocket类 290 11.1.5 NIO及相关技术 290 11.2 练习 291 11.2.1 ServerSocket与Socket示例: 开发一个Server-Client模型的程序 291 11.2.2 多点传送示例 293 11.2.3 打造你自己的QQ 295 11.3 小结 315 第12章 找个好管家——JMX 317 12.1 讲解 318 12.1.1 什么是 JMX 318 12.1.2 JMX基本概念 318 12.1.3 JMX管理应用开发 323 12.2 练习 325 12.2.1 建立JMX开发环境 325 12.2.2 创建拼词游戏程序 326 12.2.3 分布式管理扇形绘制Applet 程序 330 12.3 小结 350 第13章 让我们更专业——软件工程的基本概念 351 13.1 讲解 352 13.1.1 软件工程的基本概念 352 13.1.2 版本控制系统 356 13.1.3 统一建模语言 359 13.2 练习 360 13.2.1 建立CVS的使用环境和基本操作 360 13.2.2 使用标记和分支 373 13.3 使用UML建模 376 13.3.1 对基本结构建模 376 13.3.2 对高级结构建模 381 13.4 小结 387 索引... 389
├─第一阶段 │      源码+ppt.rar │      高并发编程第一阶段01讲、课程大纲及主要内容介绍.wmv │      高并发编程第一阶段02讲、简单介绍什么是线程.wmv │      高并发编程第一阶段03讲、创建并启动线程.mp4 │      高并发编程第一阶段04讲、线程生命周期以及start方法源码剖析.mp4 │      高并发编程第一阶段05讲、采用多线程方式模拟银行排队叫号.mp4 │      高并发编程第一阶段06讲、用Runnable接口将线程的逻辑执行单元从控制中抽取出来.mp4 │      高并发编程第一阶段07讲、策略模式在Thread和Runnable中的应用分析.mp4 │      高并发编程第一阶段08讲、构造Thread对象你也许不知道的几件事.mp4 │      高并发编程第一阶段09讲、多线程与JVM内存结构的关系,虚拟机栈实验.mp4 │      高并发编程第一阶段10讲、Thread构造函数StackSize详细讲解.mp4 │      高并发编程第一阶段11讲、Thread构造函数StackSize详细讲解-续.mp4 │      高并发编程第一阶段12讲、Daemon线程的创建以及使用场景分析.mp4 │      高并发编程第一阶段13讲、线程ID,优先级讲解.mp4 │      高并发编程第一阶段14讲、Thread的join方法详细介绍,结合一个典型案例.mp4 │      高并发编程第一阶段15讲、Thread中断Interrupt方法详细讲解.mp4 │      高并发编程第一阶段16讲、采用优雅的方式结束线程生命周期.mp4 │      高并发编程第一阶段17讲、Thread API综合实战,编写ThreadService实现暴力结束线程的综合实战.mp4 │      高并发编程第一阶段18讲、数据同步的引入与Synchronized的简单介绍.mp4 │      高并发编程第一阶段19讲、结合jconsole,jstack以及汇编指令认识synchronized关键字.mp4 │      高并发编程第一阶段20讲、同步代码块以及同步方法之间的区别和关系.mp4 │      高并发编程第一阶段21讲、通过实验分析This锁的存在.mp4 │      高并发编程第一阶段22讲、通过实验分析Class锁的存在.mp4 │      高并发编程第一阶段23讲、多线程死锁分析,案例介绍.mp4 │      高并发编程第一阶段24讲、线程间通信快速入门,使用wait和notify进行线程间的数据通信.mp4 │      高并发编程第一阶段25讲、多Produce多Consume之间的通讯导致出现程序假死的原因分析.mp4 │      高并发编程第一阶段26讲、多线程下的生产者消费者模型,以及详细介绍notifyAll方法.mp4 │      高并发编程第一阶段27讲、wait和sleep的本质区别是什么,深入分析(面试常见问题).mp4 │      高并发编程第一阶段28讲、线程生产者消费者的综合实战结合Java8语法.mp4 │      高并发编程第一阶段29讲、如何实现一个自己的显式锁Lock精讲上.mp4 │      高并发编程第一阶段30讲、如何实现一个自己的显式锁Lock精讲下(让锁具备超时功能).mp4 │      高并发编程第一阶段31讲、如何给你的应用程序注入钩子程序,Linux下演示.mp4 │      高并发编程第一阶段32讲、如何捕获线程运行期间的异常.mp4 │      高并发编程第一阶段33讲、ThreadGroup API介绍之一.mp4 │      高并发编程第一阶段34讲、ThreadGroup API介绍之二.mp4 │      高并发编程第一阶段35讲、线程池原理与自定义线程池.mp4 │      高并发编程第一阶段36讲、自定义个简单的线程池并且测试.mp4 │      高并发编程第一阶段37讲、给线程池增加拒绝策略以及停止方法.mp4 │      高并发编程第一阶段38讲、给线程池增加自动扩充线程数量,以及闲时自动回收的功能.mp4 │      高并发编程第一阶段39讲、课程结束,内容回顾,下季内容预告.mp4 │ ├─第二阶段 │       Java并发编程.png │       ppt+源码.rar │       高并发编程第二阶段01讲、课程大纲及主要内容介绍.wmv │       高并发编程第二阶段02讲、介绍四种Singleton方式的优缺点在多线程情况下.wmv │       高并发编程第二阶段03讲、介绍三种高效优雅的Singleton实现方式.wmv │       高并发编程第二阶段04讲、多线程的休息室WaitSet详细介绍与知识点总结.mp4 │       高并发编程第二阶段05讲、一个解释volatile关键字作用最好的例子.mp4 │       高并发编程第二阶段06讲、Java内存模型以及CPU缓存不一致问题的引入.mp4 │       高并发编程第二阶段07讲、CPU以及CPU缓存的结构,解决高速缓存一致性问题的两种方案介绍.mp4 │       高并发编程第二阶段08讲、并发编程的三个重要概念,原子性,可见性,有序性.mp4 │       高并发编程第二阶段09讲、指令重排序,happens-before规则精讲.mp4 │       高并发编程第二阶段10讲、volatile关键字深入详解.mp4 │       高并发编程第二阶段11讲、volatile关键字总结.mp4 │       高并发编程第二阶段12讲、观察者设计模式介绍.mp4 │       高并发编程第二阶段13讲、使用观察者设计模式观察线程的生命周期.mp4 │       高并发编程第二阶段14讲、单线程执行设计模式,有一个门,始终只能一个人通过-上.mp4 │       高并发编程第二阶段15讲、单线程执行设计模式,有一个门,始终只能一个人通过-下.mp4 │       高并发编程第二阶段16讲、多线程读写锁分离设计模式讲解-上.mp4 │       高并发编程第二阶段17讲、多线程读写锁分离设计模式讲解-中.mp4 │       高并发编程第二阶段18讲、多线程读写锁分离设计模式讲解-下.mp4 │       高并发编程第二阶段19讲、多线程不可变对象设计模式Immutable-上.mp4 │       高并发编程第二阶段20讲、多线程不可变对象设计模式Immutable-下.mp4 │       高并发编程第二阶段21讲、多线程Future设计模式详细介绍-上.mp4 │       高并发编程第二阶段22讲、多线程Future设计模式详细介绍-下.mp4 │       高并发编程第二阶段23讲、第二阶段课程答疑学员问题.mp4 │       高并发编程第二阶段24讲、Guarded Suspension设计模式-上.mp4 │       高并发编程第二阶段25讲、Guarded Suspension设计模式-下.mp4 │       高并发编程第二阶段26讲、ThreadLocal使用详解,深入原理介绍.mp4 │       高并发编程第二阶段27讲、多线程运行上下文设计模式介绍.mp4 │       高并发编程第二阶段28讲、使用ThreadLocal重新实现一个上下文设计模式.mp4 │       高并发编程第二阶段29讲、多线程Balking设计模式-上.mp4 │       高并发编程第二阶段30讲、多线程Balking设计模式-下.mp4 │       高并发编程第二阶段31讲、多线程Producer and Consumer设计模式.mp4 │       高并发编程第二阶段32讲、多线程Count Down设计模式.mp4 │       高并发编程第二阶段33讲、多线程Thread-Per-Message设计模式.mp4 │       高并发编程第二阶段34讲、多线程Two Phase Termination设计模式-上.mp4 │       高并发编程第二阶段35讲、多线程Two Phase Termination设计模式-下.mp4 │       高并发编程第二阶段36讲、多线程Worker-Thread设计模式-上.mp4 │       高并发编程第二阶段37讲、多线程Worker-Thread设计模式-上.mp4 │       高并发编程第二阶段38讲、多线程Active Objects设计模式(接受异步消息的主动对象)-上.mp4 │       高并发编程第二阶段39讲、多线程Active Objects设计模式(接受异步消息的主动对象)-中.mp4 │       高并发编程第二阶段40讲、多线程Active Objects设计模式(接受异步消息的主动对象)-下.mp4 │       高并发编程第二阶段41讲、多线程设计模式内容回顾与总结.mp4 │       高并发编程第二阶段42讲、ClassLoader课程大纲介绍.mp4 │       高并发编程第二阶段43讲、类加载的过程以及类主动使用的六种情况详细介绍.mp4 │       高并发编程第二阶段44讲、被动引用和类加载过程的练习巩固训练题.mp4 │       高并发编程第二阶段45讲、ClassLoader加载阶段发生的故事.mp4 │       高并发编程第二阶段46讲、ClassLoader链接阶段(验证,准备,解析)过程详细介绍.mp4 │       高并发编程第二阶段47讲、ClassLoader初始化阶段详细介绍clinit.mp4 │       高并发编程第二阶段48讲、JVM内置三大类加载器的详细介绍.mp4 │       高并发编程第二阶段49讲、自定义类加载器ClassLoader顺便问候了一下世界.mp4 │       高并发编程第二阶段50讲、ClassLoader父委托机制详细介绍.mp4 │       高并发编程第二阶段51讲、加密解密类加载实战演示.mp4 │       高并发编程第二阶段52讲、加密解密类加载实战演示-续.mp4 │       高并发编程第二阶段53讲、ClassLoader打破双父亲委托机制,重写loadClass实战练习.mp4 │       高并发编程第二阶段54讲、ClassLoader命名空间,运行时包,类卸载详细介绍.mp4 │       高并发编程第二阶段55讲、线程上下文类加载器以及数据库驱动案例分析.mp4 │       └─第三阶段        Java并发编程.png        Java高并发第三阶段(JUC).png        高并发编程第三阶段01讲 AtomicInteger多线程下测试讲解.mkv        高并发编程第三阶段02讲 AtomicInteger API详解,以及CAS算法详细介绍.mkv        高并发编程第三阶段03讲 利用CAS构造一个TryLock自定义显式锁.mp4        高并发编程第三阶段04讲 利用CAS构造一个TryLock自定义显式锁-增强并发情况下.mp4        高并发编程第三阶段05讲 AtomicBoolean源码分析.mp4        高并发编程第三阶段06讲 AtomicLong源码分析.mp4        高并发编程第三阶段07讲 AtomicReference详解,CAS算法带来的ABA问题详解.mp4        高并发编程第三阶段08讲 AtomicStampReference详解,解决CAS带来的ABA问题.mp4        高并发编程第三阶段09讲 AtomicIntegerArray,AtomicLongArray,AtomicReferenceArray讲解.mp4        高并发编程第三阶段10讲 AtomicIntegerFieldUpdater,AtomicLongFieldUpdater,AtomicReferenceFieldUpdater讲解.mp4        高并发编程第三阶段11讲 AtomicXXXFieldUpdater源码分析及使用场景分析.mp4        高并发编程第三阶段12讲 sun.misc.Unsafe介绍以及几种Counter方案性能对比.mp4        高并发编程第三阶段13讲 一个JNI程序的编写,通过Java去调用C,C++程序.mp4        高并发编程第三阶段14讲 Unsafe中的方法使用,一半是天使,一半是魔鬼的Unsafe.mp4        高并发编程第三阶段15讲 Unsafe背后的汇编指令,牛逼男人背后的女人_.mp4        高并发编程第三阶段16讲 CountDownLatch经典案例讲解-上_.mp4        高并发编程第三阶段17讲 CountDownLatch经典案例讲解及API精讲-中_.mp4        高并发编程第三阶段18讲 CountDownLatch经典案例讲解如何给离散平行任务增加逻辑层次关系-下_.mp4        高并发编程第三阶段19讲 CyclicBarrier工具的使用场景介绍_.mp4        高并发编程第三阶段20讲 CyclicBarrier vs CountDownLatch_.mp4        高并发编程第三阶段21讲 Exchanger工具的使用以及常见问题分析-上_.mp4        高并发编程第三阶段22讲 Exchanger工具的使用以及常见问题分析-下_.mp4        高并发编程第三阶段23讲 Semaphore工具的介绍以及借助于Semaphore构造一个Lock_.mp4        高并发编程第三阶段24讲 Semaphore工具API详细介绍-上_.mp4        高并发编程第三阶段25讲 Semaphore工具API详细介绍-下_.mp4        高并发编程第三阶段26讲 Lock&ReentrantLock详细讲解_.mp4        高并发编程第三阶段27讲 ReadWriteLock&ReentrantReadWriteLock详细讲解_.mp4        高并发编程第三阶段28讲 Condition初步使用,提出几个疑问_.mp4        高并发编程第三阶段29讲 关于Condition疑问的几个小实验,对比Wait&Notify_.mp4        高并发编程第三阶段30讲 使用Condition实现一个多线程下的Producer-Consumer_.mp4        高并发编程第三阶段31讲 JDK8-StampedLock详细介绍-上_.mp4        高并发编程第三阶段32讲 JDK8-StampedLock详细介绍-下.mp4        高并发编程第三阶段33讲 ForkJoin框架之RecursiveTask_.mp4        高并发编程第三阶段34讲 ForkJoin框架之RecursiveAction_.mp4        高并发编程第三阶段35讲 Phaser工具的实战案例使用第一部分_.mp4        高并发编程第三阶段36讲 Phaser工具的实战案例使用第二部分_.mp4        高并发编程第三阶段37讲 Phaser工具的实战案例使用第三部分_.mp4        高并发编程第三阶段38讲 Executor&ExecutorService讲解_.mp4        高并发编程第三阶段39讲 ThreadPoolExecutor七大构造参数详细讲解_.mp4        高并发编程第三阶段40讲 ThreadPoolExecutor关闭(很重要)精讲_.mp4        高并发编程第三阶段41讲 newCache&newFixed&single ExecutorService详解_.mp4        高并发编程第三阶段42讲 newWorkStealingPool ExecutorService详解_.mp4        高并发编程第三阶段43讲 Scheduler的前奏Timer&Linux Crontab & quartz比较_.mp4        高并发编程第三阶段44讲 ExecutorService API详细讲解-上_.mp4        高并发编程第三阶段45讲 ExecutorService 四大内置拒绝策略深入探究_.mp4        高并发编程第三阶段46讲 ExecutorService API详细讲解-中_.mp4        高并发编程第三阶段47讲 ExecutorService API详细讲解-下_.mp4        高并发编程第三阶段48讲 Future&Callable详细讲解-上_.mp4        高并发编程第三阶段49讲 Future&Callable详细讲解-下_.mp4        高并发编程第三阶段50讲 CompletionService详细介绍_.mp4        高并发编程第三阶段51讲 ScheduledExecutorService详细讲解-上_.mp4        高并发编程第三阶段52讲 ScheduledExecutorService详细讲解-下_.mp4        高并发编程第三阶段53讲 知识回顾与串联_.mp4        高并发编程第三阶段54讲 课程问题答疑,ExecutorService中的陷阱_.mp4        高并发编程第三阶段55讲 CompletableFuture的使用精讲(体验)-1_.mp4        高并发编程第三阶段56讲 CompletableFuture的使用精讲(构建)-2_.mp4        高并发编程第三阶段57讲 CompletableFuture的使用精讲(熟练)-3_.mp4        高并发编程第三阶段58讲 CompletableFuture的使用精讲(深入)-4_.mp4        高并发编程第三阶段59讲 CompletableFuture的使用精讲(掌握)-5_.mp4        高并发编程第三阶段60讲 LinkedList和有序LinkedList的实现_.mp4        高并发编程第三阶段61讲 跳表数据结构的Java实现-1_.mp4        高并发编程第三阶段62讲 跳表数据结构的Java实现-2_.mp4        高并发编程第三阶段63讲 跳表数据结构的Java实现(解决Bug)-3_.mp4        高并发编程第三阶段64讲 ArrayBlockingList详细讲解_.mp4        高并发编程第三阶段65讲 PriorityBlockingQueue详细讲解_.mp4        高并发编程第三阶段66讲 LinkedBlockingQueue详细讲解_.mp4        高并发编程第三阶段67讲 SynchronousQueue详细讲解_.mp4        高并发编程第三阶段68讲 DelayQueue详细讲解_.mp4        高并发编程第三阶段69讲 LinkedBlockingDeque详细讲解_.mp4        高并发编程第三阶段70讲 LinkedTransferQueue详细讲解_.mp4        高并发编程第三阶段71讲 七大BlockingQueue的特点总结,可以不用详细看_.mp4        高并发编程第三阶段72讲 ConcurrentHashMap性能测试以及JDK1.7原理讲解_.mp4        高并发编程第三阶段73讲 ConcurrentHashMap性能测试以及JDK1.8原理讲解_.mp4        高并发编程第三阶段74讲 ConcurrentSkipListMap详细讲解_.mp4        高并发编程第三阶段75讲 ConcurrentSkipListMap vs ConcurrentHashMap_.mp4        高并发编程第三阶段76讲 ConcurrentLinkedQueue&ConcurrentLinkedDeque_.mp4        高并发编程第三阶段77讲 CopyOnWriteArrayList&CopyOnWriteArraySet源码分析_.mp4        高并发编程第三阶段78讲 ConcurrentLinkedList vs CopyOnWriteArrayList vs SynchronizedList性能对比_.mp4        高并发编程第三阶段79讲 实现一个高并发的无锁队列(Lock-Free).mp4        高并发编程第三阶段80讲 总结与回顾,闲聊与感谢.mp4
JAVA线程基本学习, JAVA多线程的特性= 线程池: 本质上是一个对象池, 用来管理线程资源. 在任务执行前, 需要从线程池中拿出线程来执行. 在任务执行完成之后, 需要把线程放回线程池. 线程池好处: 降低资源的消耗, 线程本身是一种资源, 创建和销毁都会消耗CPU内存, 频繁的创建和销毁会浪内存. 提高任务执行的响应速度, 任务执行时, 可以不必等线程创建完成之后在执行,可以直接获取线程执行任务. 提高线程的可管理性, 线程不能无限制的创建, 需要进行统一的分配, 调优和监控. 不适用线程池坏处: 频繁的线程创建和销毁会占用更多的CPU和内积. 频繁的创建和销毁会对gc产生比较大的压力. 线程太多,线程切换带来的开销将不可忽视. 线程太少, 多核CPU得不到充分利用, 浪费资源. 线程池实现原理: 分为三部分: 核心线程池 线程池 队列 拒绝策略 主要流程: 1.判断核心线程池是否已满, 如果不是, 则创建线程执行任务 2.如果核心线程池满了, 判断队列是否满了, 如果队列没满, 将任务放在队列中 3.如果队列满了, 则判断线程池是否已满, 如果没满, 创建线程执行任务 4.如果线程池满了, 按照拒绝策略对任务进行处理 JDK中提供了一个线程池工厂: Executors ,很多工厂方法, 可以创建多种线程池 1.单一线程池 ExecutorService newSingleThreadExecutor = Executors.newSingleThreadExecutor(); 该线程池只有一个线程, 若多个任务被提交到此线程池, 那么会被缓冲到队列,当线程空闲时,按照FIFO的方式进行处理. 2.固定数量线程池 ExecutorService newFixedThreadPool = Executors.newFixedThreadPool(5); 和创建单一线程池类似, 不同的是线程池中有多个线程, 可以并行处理任务, 若多个线程任务被提交到此线程池, 会有以下执行过程: 如果线程的数量未达到指定数量, 则创建新线程执行任务. 如果线程池的数量达到了指定数量, 而且此时有线程是空闲的, 则取出空闲线程来执行任务. 如果没有线程是空闲的, 则将任务缓冲到队列, 当线程空闲的时候, 按照FIFO的方式进行处理. FIFO: 先进先出 3.带缓冲的线程池 该线程池中, 核心线程池长度为0, 线程池最大长度为Integer.MAX_VALUE.以SynchronousQueue作为等待队列, 从而每次往队列中插入一个元素, 必须等待另一个线程从这个队列删除一个元素. 定时调度 4.定时调度的线程池 ExecutorService newCachedThreadPool = Executors.newScheduledThreadPool(); ScheduledExecutorService newScheduledThreadPool = Executors.newScheduledThreadPool(5); ExecutorService newWorkStealingPool = Executors.newWorkStealingPool() 线程池家族 线程池的顶层接口是Executor, 这个接口定义了一个核心方法executor(Runnable command), 这个方法最后被ThreadPoolExecutor类实现, 这个方法用来传入任务, 并且该类是线程池的核心类, 构造方法如下 : public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue); public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory); public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,RejectedExecutionHandler handler); public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueu, ThreadFactory threadFactory,RejectedExecutionHandler handler); 参数意义: CorePoolSize: 核心线程池大小, 如果核心线程池有空闲的位置, 新的任务就会被核心线程池新建一个线程执行, 执行完毕不会销毁线程, 线程会进入缓冲队列等待再次被运行 MaximunPoolSize: 线程池能创建最大的线程数量, 如果核心线程池和缓冲队列都已经满了, 新的任务就会进来创建新的线程来执行, 但是数量不能超过maximunPoolSize, 否则采取拒绝接受任务策略 KeepAliveTime: 非核心线程能够空闲的最长时间, 超过时间, 线程终止, 这个参数默认只有在线程数量超过核心线程池大小时, 才会起作用. Unit: 时间单位, 和keepAliveTime配合使用. WorkQueue: 缓冲队列, 用来存放等待被执行的任务 ThreadFactory: 线程工厂, 用来创建线程, 一般有三个选择 ArrayBlockingQueue LinkedBlockingQueue SynchronousQueue Handler :拒绝处理策略, 线程数量大于最大线程数量就会拒绝处理策略, 四种策略为 ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。 ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常。 ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程) ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务 Executor接口有一个子接口ExecutorService, ExecutorService的实现类AbstracExecutorService, 而ThreadPoolExecutor正是AbstracExecutorService的子类. ThreadPoolExecutor还有两个常用的方法shutdown和submit,两者都用来关闭线程池, 但是submit有一个结果返回. 线程池任务执行 当执行executor(Runnable command)方法后, 传入一个任务, public void execute(Runnable command) { if (command == null) throw new NullPointerException(); if (poolSize >= corePoolSize || !addIfUnderCorePoolSize(command)) { if (runState == RUNNING && workQueue.offer(command)) { if (runState != RUNNING || poolSize == 0) ensureQueuedTaskHandled(command); } else if (!addIfUnderMaximumPoolSize(command)) reject(command); } } (传入的对象实现了Runnable接口, 这就解释了: 为什么实现多线程的方式中继承Thread类不能使用线程池了) 执行过程: 首先判断任务是否为空, 为空抛出空指针异常, 否则执行下一个判断: 当前线程数量是否小于核心线程池线程数量, 是,则执行addIfUbderCorePollSize(command)方法, 在核心线程池中创建新的线程, 并且执行这个任务 总结: 如果当前线程池中的数目小于corePollSize, 则每来一个任务, 就会创建一个新的线程去执行这个任务 如果当前线程池中的线程数目>=corePollSize, 则每来一个任务, 会尝试将其添加到缓冲队列中, 如果添加成功, 则该任务会等待空闲线程将其取出去执行, 如果添加失败(一般是以为任务队列已经满了), 则会尝试创建新的线程去执行这个任务. 如果当前线程池中的线程数目达到maximunPoolSize, 则会采取任务拒绝策略进行处理. 如果线程池中的线程数量大于corePoolSize时, 如果某线程空闲时间超过keepAliveTime, 线程将被终止, 直至线程池中的线程数目小于等于corePoolSize; 如果允许核心池中的线程设置存活时间, 那么核心池中的线程空闲时间超过keepAliveTime, 线程也会被终止.
ScheduledExecutorServiceJava中的一个接口,它是用来执行定时任务的。与Timer相比,ScheduledExecutorService提供了更多的灵活性和功能。\[1\]与Timer不同,ScheduledExecutorService可以执行指定时间点的任务,而不仅仅是延迟执行任务。此外,ScheduledExecutorService的方法返回值为ScheduledFuture,而不是void。\[1\]要使用ScheduledExecutorService,可以通过创建ScheduledThreadPoolExecutor对象来实现。\[3\]可以通过Executors类的newScheduledThreadPool方法来创建ScheduledThreadPoolExecutor对象,并指定后台线程的数量。\[2\]通过ScheduledExecutorService,可以实现延迟执行任务或者定期执行任务的功能。 #### 引用[.reference_title] - *1* *2* [ScheduledExecutorService使用介绍](https://blog.csdn.net/weixin_42683476/article/details/127638096)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [Java多线程的ScheduledExecutorService使用](https://blog.csdn.net/qq_40454136/article/details/123799018)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值