任务调度线程池

简介: 任务调度线程池

Timer  

在『任务调度线程池』功能加入之前,可以使用 java.util.Timer 来实现定时功能,Timer 的优点在于简单易用,但 由于所有任务都是由同一个线程来调度,因此所有任务都是串行执行的,同一时间只能有一个任务在执行,前一个 任务的延迟或异常都将会影响到之后的任务。

1. public class Test {
2. public static void main(String[] args) {
3. Timer timer = new Timer();
4. TimerTask task1 = new TimerTask() {
5. @Override
6. public void run() {
7.                 log.debug("task 1");
8.                 sleep(2);
9.             }
10.         };
11. TimerTask task2 = new TimerTask() {
12. @Override
13. 
14. public void run() {
15.                 log.debug("task 2");
16.             }
17.         };
18. // 使用 timer 添加两个任务,希望它们都在 1s 后执行
19. // 但由于 timer 内只有一个线程来顺序执行队列中的任务,
20. //因此『任务1』的延时,影响了『任务2』的执行
21. 
22.         timer.schedule(task1, 1000);
23.         timer.schedule(task2, 1000);
24.     }
25. 
26. }

输出

20:46:09.444 c.TestTimer [main] - start...

20:46:10.447 c.TestTimer [Timer-0] - task 1

20:46:12.448 c.TestTimer [Timer-0] - task 2  

ScheduledExecutorService

线程池支持定时以及周期性执行任务,创建一个corePoolSize为传入参数,最大线程数为整形的最大数的线程池

1. public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
2. return new ScheduledThreadPoolExecutor(corePoolSize);
3.     }

ScheduledThreadPoolExecutor类的构造:

1. public ScheduledThreadPoolExecutor(int corePoolSize) {
2. super(corePoolSize, Integer.MAX_VALUE, 0, TimeUnit.NANOSECONDS,
3. new DelayedWorkQueue());
4.     }
1. ScheduledExecutorService executor = Executors.newScheduledThreadPool(2);
2. 
3. // 添加两个任务,希望它们都在 1s 后执行
4. 
5. executor.schedule(() -> {
6.      System.out.println("任务1,执行时间:" + new Date());
7. try { Thread.sleep(2000); } catch (InterruptedException e) { }
8. }, 1000, TimeUnit.MILLISECONDS);
9. 
10. executor.schedule(() -> {
11.      System.out.println("任务2,执行时间:" + new Date());
12. }, 1000, TimeUnit.MILLISECONDS);

输出

任务2,执行时间:Fri Jun 23 18:04:46 CST 2023

任务1,执行时间:Fri Jun 23 18:04:46 CST 2023

scheduleAtFixedRate 例子:

构造方法

1. public ScheduledFuture<?> scheduleAtFixedRate(Runnable command,
2. long initialDelay,
3. long period,
4.                                                   TimeUnit unit);

提交一个定期操作,该操作在给定的初始延迟后首先启用,随后在给定的时间段内启用;也就是说,执行将在 之后开始initialDelay,然后 、 initialDelay + 2 * period然后 initialDelay + period,依此类推。

1. ScheduledExecutorService pool = Executors.newScheduledThreadPool(1);
2. 
3. log.debug("start...");
4. 
5. pool.scheduleAtFixedRate(() -> {
6.  log.debug("running...");
7. }, 1, 1, TimeUnit.SECONDS);

输出

21:45:43.167 c.TestTimer [main] - start...

21:45:44.215 c.TestTimer [pool-1-thread-1] - running...

21:45:45.215 c.TestTimer [pool-1-thread-1] - running...

21:45:46.215 c.TestTimer [pool-1-thread-1] - running...

21:45:47.215 c.TestTimer [pool-1-thread-1] - running...

scheduleAtFixedRate 例子(任务执行时间超过了间隔时间):  

1. ScheduledExecutorService pool = Executors.newScheduledThreadPool(1);
2. 
3. log.debug("start...");
4. 
5. pool.scheduleAtFixedRate(() -> {
6.  log.debug("running...");
7.  sleep(2);
8. }, 1, 1, TimeUnit.SECONDS);

输出分析:一开始,延时 1s,接下来,由于任务执行时间 > 间隔时间,间隔被『撑』到了 2s

21:44:30.311 c.TestTimer [main] - start...

21:44:31.360 c.TestTimer [pool-1-thread-1] - running...

21:44:33.361 c.TestTimer [pool-1-thread-1] - running...

21:44:35.362 c.TestTimer [pool-1-thread-1] - running...

21:44:37.362 c.TestTimer [pool-1-thread-1] - running...  

scheduleWithFixedDelay 例子:

1. ScheduledExecutorService pool = Executors.newScheduledThreadPool(1);
2. 
3. log.debug("start...");
4. 
5. pool.scheduleWithFixedDelay(()-> {
6.  log.debug("running...");
7.  sleep(2);
8. }, 1, 1, TimeUnit.SECONDS);

输出分析:一开始,延时 1s,scheduleWithFixedDelay 的间隔是 上一个任务结束 -> 延时 -> 下一个任务开始 所 以间隔都是 3s

21:40:55.078 c.TestTimer [main] - start...

21:40:56.140 c.TestTimer [pool-1-thread-1] - running...

21:40:59.143 c.TestTimer [pool-1-thread-1] - running...

21:41:02.145 c.TestTimer [pool-1-thread-1] - running...

21:41:05.147 c.TestTimer [pool-1-thread-1] - running...

评价 整个线程池表现为:线程数固定,任务数多于线程数时,会放入无界队列排队。任务执行完毕,这些线 程也不会被释放。用来执行延迟或反复执行的任务

正确处理执行任务异常

方法1:主动捉异常

1. ExecutorService pool = Executors.newFixedThreadPool(1);
2. pool.submit(() -> {
3. try {
4.      log.debug("task1");
5. int i = 1 / 0;
6.  } catch (Exception e) {
7.      log.error("error:", e);
8.  }
9. })

方法2:使用 Future  

1. ExecutorService pool = Executors.newFixedThreadPool(1);
2. 
3. Future<Boolean> f = pool.submit(() -> {
4.      log.debug("task1");
5. int i = 1 / 0;
6. return true;
7. });
8. 
9. log.debug("result:{}", f.get());

相关文章
|
7月前
|
缓存 监控 Java
Java并发编程:线程池与任务调度
【4月更文挑战第16天】Java并发编程中,线程池和任务调度是核心概念,能提升系统性能和响应速度。线程池通过重用线程减少创建销毁开销,如`ThreadPoolExecutor`和`ScheduledThreadPoolExecutor`。任务调度允许立即或延迟执行任务,具有灵活性。最佳实践包括合理配置线程池大小、避免过度使用线程、及时关闭线程池和处理异常。掌握这些能有效管理并发任务,避免性能瓶颈。
64 0
|
算法 Java Go
【多线程系列-03】深入理解java中线程的生命周期,任务调度
【多线程系列-03】深入理解java中线程的生命周期,任务调度
180 0
|
Java 调度
ScheduledExecutorService:多线程任务调度
ScheduledExecutorService:多线程任务调度
784 0
ScheduledExecutorService:多线程任务调度
|
缓存 Java 调度
【Android 异步操作】线程池 ( 线程池作用 | 线程池种类 | 线程池工作机制 | 线程池任务调度源码解析 )
【Android 异步操作】线程池 ( 线程池作用 | 线程池种类 | 线程池工作机制 | 线程池任务调度源码解析 )
139 0
|
调度
任务调度(四)——ScheduledExecutorService替代Timer,实现多线程任务调度
       上篇博文《任务调度(三)——Timer的替代品ScheduledExecutorService简介》已经对ScheduledExecutorService做了简单介绍,其实使用ScheduledExecutorService来替代Timer也是迫不得已的事情。
1244 0
|
并行计算 调度 编译器
OpenMP 中的线程任务调度
OpenMP中任务调度主要针对并行的for循环,当循环中每次迭代的计算量不相等时,如果简单地给各个线程分配相同次数的迭代,则可能会造成各个线程计算负载的不平衡,影响程序的整体性能。 如下面的代码中,如果每个线程执行的任务数量平均分配,有的线程会结束早,有的线程结束晚: 1 #include...
1370 0
|
2月前
|
存储 消息中间件 资源调度
C++ 多线程之初识多线程
这篇文章介绍了C++多线程的基本概念,包括进程和线程的定义、并发的实现方式,以及如何在C++中创建和管理线程,包括使用`std::thread`库、线程的join和detach方法,并通过示例代码展示了如何创建和使用多线程。
58 1
C++ 多线程之初识多线程
|
2月前
|
Java 开发者
在Java多线程编程中,创建线程的方法有两种:继承Thread类和实现Runnable接口
【10月更文挑战第20天】在Java多线程编程中,创建线程的方法有两种:继承Thread类和实现Runnable接口。本文揭示了这两种方式的微妙差异和潜在陷阱,帮助你更好地理解和选择适合项目需求的线程创建方式。
27 3
|
2月前
|
Java 开发者
在Java多线程编程中,选择合适的线程创建方法至关重要
【10月更文挑战第20天】在Java多线程编程中,选择合适的线程创建方法至关重要。本文通过案例分析,探讨了继承Thread类和实现Runnable接口两种方法的优缺点及适用场景,帮助开发者做出明智的选择。
23 2
|
2月前
|
Java
Java中多线程编程的基本概念和创建线程的两种主要方式:继承Thread类和实现Runnable接口
【10月更文挑战第20天】《JAVA多线程深度解析:线程的创建之路》介绍了Java中多线程编程的基本概念和创建线程的两种主要方式:继承Thread类和实现Runnable接口。文章详细讲解了每种方式的实现方法、优缺点及适用场景,帮助读者更好地理解和掌握多线程编程技术,为复杂任务的高效处理奠定基础。
38 2