• 定时且周期性的任务研究II--ScheduledThreadPoolExecutor


    http://victorzhzh.iteye.com/blog/1011635

    上一篇中我们看到了Timer的不足之处,本篇我们将围绕这些不足之处看看ScheduledThreadPoolExecutor是如何优化的。

    为了研究方便我们需要两个类:

    Java代码  收藏代码
    1. public class Task1 implements Callable<String> {  
    2.   
    3.     @Override  
    4.     public String call() throws Exception {  
    5.         String base = "abcdefghijklmnopqrstuvwxyz0123456789";  
    6.         Random random = new Random();  
    7.         StringBuffer sb = new StringBuffer();  
    8.         for (int i = 0; i < 10; i++) {  
    9.             int number = random.nextInt(base.length());  
    10.             sb.append(base.charAt(number));  
    11.         }  
    12.         System.out.println("Task1 running: " + new Date());  
    13.         return sb.toString();  
    14.     }  
    15.   
    16. }  

     生成含有10个字符的字符串,使用Callable接口目的是我们不再任务中直接输出结果,而主动取获取任务的结果

    Java代码  收藏代码
    1. public class LongTask implements Callable<String> {  
    2.   
    3.     @Override  
    4.     public String call() throws Exception {  
    5.         System.out.println("LongTask running: "+new Date());  
    6.         TimeUnit.SECONDS.sleep(10);  
    7.         return "success";  
    8.     }  
    9.   
    10. }  

    长任务类,这里我们让任务沉睡10秒然后返回一个“success”

    下面我们来分析一下ScheduledThreadPoolExecutor:

    1、Timer中单线程问题是否在ScheduledThreadPoolExecutor中存在?

    我们先来看一下面的程序:

    Java代码  收藏代码
    1. public class ScheduledThreadPoolExec {  
    2.     public static void main(String[] args) throws InterruptedException,  
    3.             ExecutionException {  
    4.         <strong><span style="color: #ff0000;">ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(  
    5.                 2);</span>  
    6. </strong>  
    7.   
    8.   
    9.         ScheduledFuture future1 = executor.schedule(new Task1(), 5,  
    10.                 TimeUnit.SECONDS);  
    11.         ScheduledFuture future2 = executor.schedule(new LongTask(), 3,  
    12.                 TimeUnit.SECONDS);  
    13.   
    14.         BlockingQueue<ScheduledFuture> blockingQueue = new ArrayBlockingQueue<ScheduledFuture>(  
    15.                 2, true);  
    16.         blockingQueue.add(future2);  
    17.         blockingQueue.add(future1);  
    18.   
    19.         System.out.println(new Date());  
    20.         while (!blockingQueue.isEmpty()) {  
    21.             ScheduledFuture future = blockingQueue.poll();  
    22.             if (!future.isDone())  
    23.                 blockingQueue.add(future);  
    24.             else  
    25.                 System.out.println(future.get());  
    26.         }  
    27.         System.out.println(new Date());  
    28.         executor.shutdown();  
    29.     }  
    30.   
    31. }  

     首先,我们定义了一个ScheduledThreadPoolExecutor它的池长度是2。接着提交了两个任务:第一个任务将延迟5秒执行,第二个任务将延迟3秒执行。我们建立了一个BlockingQueue,用它来存储了ScheduledFuture,使用ScheduledFuture可以获得任务的执行结果。在一个while循环中,我们每次将一个ScheduledFuture从队列中弹出,验证它是否被执行,如果没有被执行则再次将它加入队列中,如果被执行了,这使用ScheduledFuture的get方法获取任务执行的结果。看一下执行结果:

    Java代码  收藏代码
    1. Thu Apr 21 19:23:02 CST 2011  
    2. LongTask running: Thu Apr 21 19:23:05 CST 2011  
    3. Task1 running: Thu Apr 21 19:23:07 CST 2011  
    4. h1o2wd942e  
    5. success  
    6. Thu Apr 21 19:23:15 CST 2011  

     我们看到长任务先运行,因为长任务只等待了3秒,然后是输出字符串的任务运行,两个任务开始时间相差2秒,而先输出了字符串而后才是长任务运行的结果success,最后我们查看一下整体的开始和结束时间相差了13秒。

    这说明在ScheduledThreadPoolExecutor中它不是以一个线程运行任务的,而是以多个线程,如果用一个线程运行任务,那么长任务运行完之前是不会运行输出字符串任务的。其实这个“多个任务“是我们自己指定的注意一下标红的代码,如果我们把这行代码改为:

    Java代码  收藏代码
    1. ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(1);  

     那么运行结果就会发生变化,

    Java代码  收藏代码
    1. Thu Apr 21 19:36:56 CST 2011  
    2. LongTask running: Thu Apr 21 19:36:59 CST 2011  
    3. success  
    4. Task1 running: Thu Apr 21 19:37:09 CST 2011  
    5. y981iqd0or  
    6. Thu Apr 21 19:37:09 CST 2011  

     这时其实和使用Timer运行结果是一样的。任务是在一个线程里顺序执行的。

    2、Timer中一但有运行时异常报出后续任务是否还会正常运行?

    为了研究这个问题,我们还是需要一个能够抛出异常的任务,如下:

    Java代码  收藏代码
    1. public class TimerExceptionTask extends TimerTask {  
    2.   
    3.     @Override  
    4.     public void run() {  
    5.         System.out.println("TimerExceptionTask: "+new Date());  
    6.         throw new RuntimeException();  
    7.     }  
    8.   
    9. }  

     我们对上面运行任务的代码做一点点小小的修改,先运行两个抛出异常的任务,如下:

    Java代码  收藏代码
    1. public class ScheduledThreadPoolExec {  
    2.     public static void main(String[] args) throws InterruptedException,  
    3.             ExecutionException {  
    4.         ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(  
    5.                 2);  
    6.         ScheduledFuture future1 = executor.schedule(new Task1(), 5,  
    7.                 TimeUnit.SECONDS);  
    8.         ScheduledFuture future2 = executor.schedule(new LongTask(), 3,  
    9.                 TimeUnit.SECONDS);  
    10.         <strong><span style="color: #ff0000;">executor.schedule(new TimerExceptionTask(), 1, TimeUnit.SECONDS);  
    11.         executor.schedule(new TimerExceptionTask(), 2, TimeUnit.SECONDS);</span>  
    12. </strong>  
    13.   
    14.   
    15.   
    16.         BlockingQueue<ScheduledFuture> blockingQueue = new ArrayBlockingQueue<ScheduledFuture>(  
    17.                 2, true);  
    18.         blockingQueue.add(future2);  
    19.         blockingQueue.add(future1);  
    20.   
    21.         System.out.println(new Date());  
    22.         while (!blockingQueue.isEmpty()) {  
    23.             ScheduledFuture future = blockingQueue.poll();  
    24.             if (!future.isDone())  
    25.                 blockingQueue.add(future);  
    26.             else  
    27.                 System.out.println(future.get());  
    28.         }  
    29.         System.out.println(new Date());  
    30.         executor.shutdown();  
    31.     }  
    32.   
    33. }  

     注意,标红的代码,如果这两个代码抛出错误后会影响后续任务,那么就应该在此终止,但是看一下结果,

    Java代码  收藏代码
    1. Thu Apr 21 19:40:15 CST 2011  
    2. TimerExceptionTask: Thu Apr 21 19:40:16 CST 2011  
    3. TimerExceptionTask: Thu Apr 21 19:40:17 CST 2011  
    4. LongTask running: Thu Apr 21 19:40:18 CST 2011  
    5. Task1 running: Thu Apr 21 19:40:20 CST 2011  
    6. v5gcf01iiz  
    7. success  
    8. Thu Apr 21 19:40:28 CST 2011  

     后续任务仍然执行,可能会有朋友说:“你上面的池设置的是2,所以很有可能是那两个抛出异常的任务都在同一个线程中执行,而另一个线程执行了后续的任务”。那我们就把ScheduledThreadPoolExecutor设置成1看看,结果如下:

    Java代码  收藏代码
    1. Thu Apr 21 19:43:00 CST 2011  
    2. TimerExceptionTask: Thu Apr 21 19:43:01 CST 2011  
    3. TimerExceptionTask: Thu Apr 21 19:43:02 CST 2011  
    4. LongTask running: Thu Apr 21 19:43:03 CST 2011  
    5. success  
    6. Task1 running: Thu Apr 21 19:43:13 CST 2011  
    7. 33kgv8onnd  
    8. Thu Apr 21 19:43:13 CST 2011  

    后续任务也执行了,所以说ScheduledThreadPoolExecutor不会像Timer那样有线程泄漏现象。

    对于周期性执行和Timer很类似这里就不再举例了。

  • 相关阅读:
    ParallelsDesktop在windows 10虚拟机重启后分辨率无法保存的问题解决方案
    Windows10 2021年5月功能更新(21H1)的三种方式
    Database "mem:XXX" not found, either pre-create it or allow remote database creation (not recommended in secure environments) [90149-200] 90149/90149 解决方案
    Win7/8下提示OpenSCManager failed 拒绝访问 解决方案
    将 Windows 更新代理更新到最新版本
    解决Eclipse中无法直接使用sun.misc.BASE64Encoder及sun.misc.BASE64Decoder的问题
    【Windows】U 盘装系统,无法格式化所选磁盘分区[错误: 0x8004242d]解决方案
    Boot Camp列表-苹果电脑Windows驱动下载
    selenium4 Timeouts is deprecated
    Selenium4实践1——对比Selenium3,Selenium4更新了什么?
  • 原文地址:https://www.cnblogs.com/senior-engineer/p/5000143.html
Copyright © 2020-2023  润新知