Main Page | Packages | Class Hierarchy | Class List | Directories | File List | Namespace Members | Class Members | File Members | Related Pages

LastExecutor.java

Go to the documentation of this file.
00001 /*
00002  * LastExecutor.java
00003  *
00004  * Copyright (C) 2005 Project SQUID, http://www.cs.helsinki.fi/group/squid/
00005  *
00006  * This file is part of Ikayaki.
00007  *
00008  * Ikayaki is free software; you can redistribute it and/or modify
00009  * it under the terms of the GNU General Public License as published by
00010  * the Free Software Foundation; either version 2 of the License, or
00011  * (at your option) any later version.
00012  *
00013  * Ikayaki is distributed in the hope that it will be useful,
00014  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00015  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
00016  * GNU General Public License for more details.
00017  *
00018  * You should have received a copy of the GNU General Public License
00019  * along with Ikayaki; if not, write to the Free Software
00020  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
00021  */
00022 
00023 package ikayaki.util;
00024 
00025 import java.util.concurrent.DelayQueue;
00026 import java.util.concurrent.Delayed;
00027 import java.util.concurrent.Executor;
00028 import java.util.concurrent.TimeUnit;
00029 
00040 public class LastExecutor implements Executor {
00041 
00045     private int delayMillis;
00046 
00051     private boolean execOnlyLast;
00052 
00057     private DelayQueue<RunDelayed> queue = new DelayQueue<RunDelayed>();
00058 
00063     private Thread workerThread = null;
00064 
00068     public LastExecutor() {
00069         this(0, true);
00070     }
00071 
00077     public LastExecutor(int delayMillis) {
00078         this(delayMillis, true);
00079     }
00080 
00087     public LastExecutor(boolean execOnlyLast) {
00088         this(0, execOnlyLast);
00089     }
00090 
00098     public LastExecutor(int delayMillis, boolean execOnlyLast) {
00099         if (delayMillis < 0) {
00100             delayMillis = 0;
00101         }
00102         this.delayMillis = delayMillis;
00103         this.execOnlyLast = execOnlyLast;
00104     }
00105 
00109     public synchronized boolean isExecOnlyLast() {
00110         return execOnlyLast;
00111     }
00112 
00117     public synchronized void setExecOnlyLast(boolean execOnlyLast) {
00118         this.execOnlyLast = execOnlyLast;
00119     }
00120 
00124     public synchronized int getDelayMillis() {
00125         return delayMillis;
00126     }
00127 
00131     public synchronized void setDelayMillis(int delayMillis) {
00132         if (delayMillis >= 0) {
00133             this.delayMillis = delayMillis;
00134         }
00135     }
00136 
00145     public synchronized void execute(Runnable command) {
00146         if (command == null) {
00147             throw new NullPointerException();
00148         }
00149         if (execOnlyLast) {
00150             queue.clear();
00151         }
00152         queue.offer(new RunDelayed(command, delayMillis)); // always successful
00153         if (workerThread == null) {
00154             workerThread = new LastExecutorThread();
00155             workerThread.start();
00156         }
00157     }
00158 
00165     public synchronized void join() throws InterruptedException {
00166         while (workerThread != null) {
00167             wait();
00168         }
00169     }
00170 
00175     public synchronized void clear() {
00176         queue.clear();
00177         if (workerThread != null) {
00178             // run an empty task, after which the execution thread will stop
00179             queue.offer(new RunDelayed(new Runnable() {
00180                 public void run() {
00181                     // DO NOTHING
00182                 }
00183             }, 0));
00184         }
00185     }
00186 
00195     private class LastExecutorThread extends Thread {
00196         public void run() {
00197             while (true) {
00198                 synchronized (LastExecutor.this) {
00199                     if (queue.size() == 0) {
00200                         workerThread = null;
00201                         LastExecutor.this.notifyAll(); // notify all who wait in LastExecutor.join()
00202                         return;
00203                     }
00204                 }
00205                 RunDelayed delayed = null;
00206                 try {
00207                     delayed = queue.take();
00208                     delayed.getRunnable().run();
00209                 } catch (Throwable t) {
00210                     System.err.println(t.getClass().getSimpleName() + " thrown by "
00211                             + delayed.getRunnable().getClass().getName());
00212                     t.printStackTrace();
00213                 }
00214             }
00215         }
00216     }
00217 
00223     private class RunDelayed implements Delayed {
00224 
00228         private long expires;
00229 
00233         private Runnable runnable;
00234 
00241         public RunDelayed(Runnable runnable, int delayMillis) {
00242             this.expires = System.currentTimeMillis() + delayMillis;
00243             this.runnable = runnable;
00244         }
00245 
00252         public long getDelay(TimeUnit unit) {
00253             return expires - System.currentTimeMillis();
00254         }
00255 
00261         public Runnable getRunnable() {
00262             return runnable;
00263         }
00264 
00273         public int compareTo(Delayed delayed) {
00274             return (int) (getDelay(TimeUnit.MILLISECONDS) - delayed.getDelay(TimeUnit.MILLISECONDS));
00275         }
00276     }
00277 
00281     public static void main(String[] args) throws InterruptedException {
00282         LastExecutor q = new LastExecutor(200, true);
00283 
00284         for (int i = 0; i < 10; i++) {
00285             final int j = i;
00286             q.execute(new Runnable() {
00287                 public void run() {
00288                     System.out.println("A " + j);
00289                 }
00290             });
00291         }
00292 
00293         q.join();
00294 
00295         for (int i = 0; i < 10; i++) {
00296             final int j = i;
00297             q.execute(new Runnable() {
00298                 public void run() {
00299                     System.out.println("B " + j);
00300                 }
00301             });
00302             Thread.sleep(50 * i);
00303         }
00304 
00305         // test that the LastExecutor will catch the Exception
00306         // and the same thread will continue to execute the next Runnable
00307         q.execute(new Runnable() {
00308             public void run() {
00309                 System.out.println("C");
00310                 try {
00311                     Thread.sleep(500);
00312                 } catch (InterruptedException e) {
00313                     e.printStackTrace();
00314                 }
00315                 throw new NullPointerException();
00316             }
00317         });
00318         Thread.sleep(300);
00319         q.execute(new Runnable() {
00320             public void run() {
00321                 System.out.println("D");
00322             }
00323         });
00324         q.join();
00325 
00326         // test that the clear() method stops the execution thread cleanly
00327         System.out.println("1");
00328         q.execute(new Runnable() {
00329             public void run() {
00330                 System.out.println("A");
00331             }
00332         });
00333         Thread.sleep(100);
00334         System.out.println("2");
00335         q.clear();
00336         System.out.println("3");
00337     }
00338 }

Generated on Fri May 6 16:00:33 2005 for Squid by  doxygen 1.4.1