ThreadPoolExecutor甚至在ScheduledFuture被取消后运行

时间:2014-01-07 17:22:55

标签: java multithreading

我正在寻找一个具有未知池大小的ScheduledThreadPoolExecutor。池大小在运行时确定,可能在1-5之间,对于此示例,我使用大小2.我们使用自定义任务,每隔一段时间执行一次方法,但该方法最终会抛出异常(我用一个简单的numTimes变量和if语句模拟了。如果抛出异常,我只想取消执行THAT特定线程!如果所有线程都被取消,我想关闭ScheduledThreadPoolExecutor。一旦numTimes == 5我模拟异常取消线程),我可以设法取消线程多种方式,但他们感觉不对。

作为旁注,我把ScheduledFuture放在任何地方只是为了解决它的问题。

    public class Test 
{
  static ScheduledThreadPoolExecutor stpe = new ScheduledThreadPoolExecutor(2);

  public static void main(String[] args) 
  { stpe.scheduleWithFixedDelay(new UpdateTask(1), 0, 1000, TimeUnit.MILLISECONDS);
    stpe.scheduleWithFixedDelay(new UpdateTask(2), 0, 5000, TimeUnit.MILLISECONDS);
//    stpe.shutdown();
  }

  public static class UpdateTask implements Runnable
  {  
     int id;
     int numTimes = 0;
     ScheduledFuture<?> t;

     public UpdateTask(int id)
     { this.id = id;
     }

     public void run()
     { System.out.println("Hello " + id + " num: " + numTimes);
       String fn = "C:\\lib" + id;
       if (numTimes++ == 5)
       { File f = new File(fn);
         f.mkdir();
         t.cancel(false);
       }
     }
  }
}

从run()或main()调用t.cancel()具有相同的效果,因为线程停止执行但程序没有停止运行。当然,这是因为尽管两个线程都不再被调度,但ThreadPoolExecutor仍然在做东西。

我尝试在stpe上调用shutdown,但它没有完成线程执行。注释掉stpe.shutdown创建了两个目录,否则它们不是。

我无法找到一种优雅的方法来取消ScheduledFuture,然后取消所有ScheduledFuture取消时的ScheduledThreadPoolExecutor。

最终方法##

我无法按照下面的答案中所述让s1.get()工作,所以我只是创建了自己的类来处理它。

public class Test 
{
  static ScheduledThreadPoolExecutor stpe = new ScheduledThreadPoolExecutor(2);
  static CancelUpdateTasks canceller;

  public static void main(String[] args) 
  { Test t = new Test();
    canceller.add(0, stpe.scheduleWithFixedDelay(new UpdateTask(0), 0, 1000, TimeUnit.MILLISECONDS));
    canceller.add(1, stpe.scheduleWithFixedDelay(new UpdateTask(1), 0, 5000, TimeUnit.MILLISECONDS));
    canceller.waitForSchedules();
    stpe.shutdown();
  }

  public Test() 
  { canceller = new CancelUpdateTasks();
  }

  public static class UpdateTask implements Runnable
  {  
     int id;
     int numTimes = 0;

     public UpdateTask(int id)
     { this.id = id;
     }

     public void run()
     { System.out.println("Hello " + id + " num: " + numTimes);
       if (numTimes++ == 5)
       { canceller.cancel(id);
       }
     }
  }

  public class CancelUpdateTasks 
  { List<ScheduledFuture<?>> scheduler;
    boolean isScheduled;

    public CancelUpdateTasks() 
    { scheduler = new ArrayList<ScheduledFuture<?>>();
      isScheduled = false;
    }

    public void waitForSchedules() 
    { int schedId = 0;
      while(isScheduled)
      { ScheduledFuture<?> schedule = scheduler.get(schedId);
        if (schedule.isCancelled())
        { if (schedId == scheduler.size() - 1)
            return;
          schedId++;
        }
        else
        { try 
          { Thread.sleep(1000);
          } 
          catch (InterruptedException e) 
          { e.printStackTrace();
          }
        }
      }
    }

    public void add(int id, ScheduledFuture<?> schedule) 
    { scheduler.add(id, schedule);
      if (!isScheduled)
        isScheduled = true;
    }

    public void cancel(int id)
    { scheduler.get(id).cancel(false);
    }

    public void cancelNow(int id)
    { scheduler.get(id).cancel(true);
    }
  }
}

1 个答案:

答案 0 :(得分:3)

您需要在池上发出关闭。 JVM将继续运行,直到只有守护程序线程处于活动状态。默认情况下, ThreadPoolExecutor 将创建非守护程序线程。

只需调用stpe.shutdown();

即可

编辑:基于OP更新

对于ScheduledThreadPoolExecugtor而言,

shutdown与普通的ThreadPoolExecutor不同。在这种情况下,shutdown会阻止重新安排任何计划任务。为了使其正常工作,您必须等待期货完成。您可以get() ScheduledFuture

进行此操作
ScheduledFuture sf1 = stpe.scheduleWithFixedDelay(new UpdateTask(1), 0, 1000, TimeUnit.MILLISECONDS);
ScheduledFuture sf2 = stpe.scheduleWithFixedDelay(new UpdateTask(2), 0, 5000, TimeUnit.MILLISECONDS);
sf1.get();
sf2.get();
stpe.shutdown();

在这种情况下,两个任务都是异步运行,主线程将首先等待sf1完成,然后等待sf2完成并最终关闭。