Interrupt a Quartz job that doing IO

如果你想中断正在执行IO的 Quartz 作业,在你使用 ​​InterruptibleChannel​​ 时这是可行的。引用一下Oracle链接:实现了这个接口的通道,是可中断的:如果一个线程在一个中断通道阻塞I/O操作,另一个线程能调用阻塞的线程的中断方法。这将导致的通道被关闭,被阻塞的线程收到一个ClosedByInterruptException,设置被阻塞的线程的中断状态。因此,获得自己工作的执行线程的作业计划,能保存供以后使用。当Quartz调度中断作业,你可以再调用该线程的interrupt()方法来停止读/写操作。这里有一个简单的例子:


package demo;   // import statements excluded for brevity   public class MyJob implements InterruptableJob {     private static Logger    LOG              = LoggerFactory.getLogger(MyJob.class);     private volatile boolean isJobInterrupted = false;     private JobKey           jobKey           = null;     private volatile Thread  thisThread;     public MyJob() {   }     public void execute(JobExecutionContext context) throws JobExecutionException {     thisThread = Thread.currentThread();     LOG.info("Thread name of the current job: " + thisThread.getName());       jobKey = context.getJobDetail().getKey();     LOG.info("Job " + jobKey + " executing at " + new Date());       try {       String fileUrl = "http://d2zwv9pap9ylyd.cloudfront.net/terracotta-3.6.1.tar.gz"; // 59 MB       String localFile = "terracotta-3.6.1.tar.gz";       download(fileUrl, localFile);     } catch (ClosedByInterruptException e) {       LOG.info("Caught ClosedByInterruptException... exiting job.");     } catch (IOException e) {       LOG.info("Caught IOException... exiting job.", e);     } finally {       if (isJobInterrupted) {         LOG.info("Job " + jobKey + " did not complete");       } else {         LOG.info("Job " + jobKey + " completed at " + new Date());       }     }   }       // this method is called by the scheduler   public void interrupt() throws UnableToInterruptJobException {     LOG.info("Job " + jobKey + "  -- INTERRUPTING --");     isJobInterrupted = true;     if (thisThread != null) {       // this called cause the ClosedByInterruptException to happen       thisThread.interrupt();     }   }     private void download(String address, String localFileName) throws ClosedByInterruptException, IOException {     URL url = new URL(address);     ReadableByteChannel src = Channels.newChannel(url.openStream());     WritableByteChannel dest = new FileOutputStream(new File(localFileName)).getChannel();     try {       System.out.println("Downloading " + address + " to " + new File(localFileName).getCanonicalPath());       int size = fastChannelCopy(src, dest);       System.out.println("Download completed! " + (size / 1024 / 1024) + " MB");     } finally {       src.close();       dest.close();     }   }       // Code copied from http://thomaswabner.wordpress.com/2007/10/09/fast-stream-copy-using-javanio-channels/   private static int fastChannelCopy(final ReadableByteChannel src, final WritableByteChannel dest) throws IOException {     final ByteBuffer buffer = ByteBuffer.allocateDirect(16 * 1024);     int count = 0;     int total = 0;     while ((count = src.read(buffer)) != -1) {       total += count;       // prepare the buffer to be drained       buffer.flip();       // write to the channel, may block       dest.write(buffer);       // If partial transfer, shift remainder down       // If buffer is empty, same as doing clear()       buffer.compact();     }     // EOF will leave buffer in fill state     buffer.flip();     // make sure the buffer is fully drained.     while (buffer.hasRemaining()) {       dest.write(buffer);     }     return total;   } }


这是我的主类,创建Quartz Scheduler和模拟预期的中断。下载将需要大约40秒完成(59MB文件)。为了看到我们的作业确实是在下载过程中中断,我们启动调度然后休息5秒。注:如果您想看到的作业完成,休息了约40秒。


package demo;   import static org.quartz.DateBuilder.nextGivenSecondDate; import static org.quartz.JobBuilder.newJob; import static org.quartz.SimpleScheduleBuilder.simpleSchedule; import static org.quartz.TriggerBuilder.newTrigger;   // other imports excluded for brevity   public class InterruptExample {     public void run() throws Exception {     final Logger log = LoggerFactory.getLogger(InterruptExample.class);       log.info("------- Initializing ----------------------");       // First we must get a reference to a scheduler     SchedulerFactory sf = new StdSchedulerFactory();     Scheduler sched = sf.getScheduler();       log.info("------- Initialization Complete -----------");       log.info("------- Scheduling Jobs -------------------");       // get a "nice round" time a few seconds in the future...     Date startTime = nextGivenSecondDate(null, 1);       JobDetail job = newJob(MyJob.class).withIdentity("myJob", "group1").build();       SimpleTrigger trigger = newTrigger().withIdentity("trigger1", "group1").startAt(startTime)         .withSchedule(simpleSchedule()).build();       sched.scheduleJob(job, trigger);       // start up the scheduler (jobs do not start to fire until     // the scheduler has been started)     sched.start();     log.info("Scheduler thread's name: " + Thread.currentThread().getName());     log.info("------- Started Scheduler -----------------");       try {       // if you want to see the job to finish successfully, sleep for about 40 seconds       Thread.sleep(5 * 1000L);       // tell the scheduler to interrupt our job       sched.interrupt(job.getKey());       Thread.sleep(3 * 1000L);     } catch (Exception e) {       e.printStackTrace();     }       log.info("------- Shutting Down ---------------------");       sched.shutdown(true);       log.info("------- Shutdown Complete -----------------");   }     public static void main(String[] args) throws Exception {     InterruptExample example = new InterruptExample();     example.run();   } }


这是日志,说明我们的作业被intterupted提早退出


1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36

INFO [main] ------- Initializing ---------------------- INFO [main] Using default implementation for ThreadExecutor INFO [main] Job execution threads will use class loader of thread: main INFO [main] Initialized Scheduler Signaller of type: class org.quartz.core.SchedulerSignalerImpl INFO [main] Quartz Scheduler v.2.1.3 created. INFO [main] RAMJobStore initialized. INFO [main] Scheduler meta-data: Quartz Scheduler (v2.1.3) 'DefaultQuartzScheduler' with instanceId 'NON_CLUSTERED' Scheduler class: 'org.quartz.core.QuartzScheduler' - running locally. NOT STARTED. Currently in standby mode. Number of jobs executed: 0 Using thread pool 'org.quartz.simpl.SimpleThreadPool' - with 10 threads. Using job-store 'org.quartz.simpl.RAMJobStore' - which does not support persistence. and is not clustered. INFO [main] Quartz scheduler 'DefaultQuartzScheduler' initialized from default resource file in Quartz package: 'quartz.properties' INFO [main] Quartz scheduler version: 2.1.3 INFO [main] ------- Initialization Complete ----------- INFO [main] ------- Scheduling Jobs ------------------- INFO [main] Scheduler DefaultQuartzScheduler_$_NON_CLUSTERED started. INFO [main] Scheduler thread's name: main INFO [main] ------- Started Scheduler ----------------- INFO [DefaultQuartzScheduler_Worker-1] Thread name of the current job: DefaultQuartzScheduler_Worker-1 NFO [DefaultQuartzScheduler_Worker-1] Job group1.myJob executing at Mon Apr 16 16:24:40 PDT 2012 Downloading ​​http://d2zwv9pap9ylyd.cloudfront.net/terracotta-3.6.1.tar.gz​​ to S:\quartz-interrupt-demo\terracotta-3.6.1.tar.gz INFO [main] Job group1.myJob -- INTERRUPTING -- INFO [DefaultQuartzScheduler_Worker-1] Caught ClosedByInterruptException... exiting job. INFO [DefaultQuartzScheduler_Worker-1] Job group1.myJob did not complete ERROR [DefaultQuartzScheduler_Worker-1] Worker thread was interrupt()'ed. java.lang.InterruptedException at java.lang.Object.wait(Native Method) at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:552) INFO [main] ------- Shutting Down --------------------- INFO [main] Scheduler DefaultQuartzScheduler_$_NON_CLUSTERED shutting down. INFO [main] Scheduler DefaultQuartzScheduler_$_NON_CLUSTERED paused. INFO [main] Scheduler DefaultQuartzScheduler_$_NON_CLUSTERED shutdown complete. INFO [main] ------- Shutdown Complete -----------------