/** * */ package vroom.common.utilities; import java.util.Collection; import java.util.concurrent.Callable; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadFactory; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicInteger; /** * <code>BatchThreadPoolExecutorNew</code> is a specialization of {@link ThreadPoolExecutor} designed to maintain a pool * of {@link Thread} that will be periodically used to execute a batch of tasks. <br/> * It provides a convenience {@link #awaitBatchCompletion()} method that waits until the task queue is empty * <p> * Creation date: May 19, 2011 - 1:24:46 PM * * @author Victor Pillac, <a href="http://uniandes.edu.co">Universidad de Los Andes</a>-<a * href="http://copa.uniandes.edu.co">Copa</a> <a href="http://www.emn.fr">Ecole des Mines de Nantes</a>-<a * href="http://www.irccyn.ec-nantes.fr/irccyn/d/en/equipes/Slp">SLP</a> * @version 1.0 */ public class BatchThreadPoolExecutorNew { // FIXME this class should not directly inherit from ThreadPoolExecutor, instead it should use one and use a more // robust synchronization scheme based on CountDownLatch. The idea would be to have the capability of adding tasks // to a temporary queue, then push all the tasks to the executor and decrement the CountDownLatch each time a task // is over. This could be achieved with a specialized thread class private final ThreadPoolExecutor mExecutor; /** A flag that will activate a debug mode in which all tasks will be run sequentially in the parent thread */ public static boolean sDebugSequential = false; private final CountDownLatch mCountDown; /** * Creates a new <code>BatchThreadPoolExecutorNew</code> * * @param size * the size of the thread pool * @param poolName * a name for the threads of the pool */ public BatchThreadPoolExecutorNew(int size, String poolName) { this(size, new NameThreadFactory(poolName)); } /** * Creates a new <code>BatchThreadPoolExecutorNew</code> * * @param size * the size of the thread pool * @param factory * the thread factory that will be used to create new threads */ public BatchThreadPoolExecutorNew(int size, ThreadFactory factory) { mExecutor = new ThreadPoolExecutor(sDebugSequential ? 1 : size, sDebugSequential ? 1 : size, 1l, TimeUnit.MINUTES, new LinkedBlockingQueue<Runnable>(), factory); mCountDown = new CountDownLatch(0); } // /** // * Submit a batch of tasks and wait for their termination. // * <p> // * The {@link Future} results should then be checked for exception handling (see {@link Future#get()}) // * </p> // * // * @param <C> // * The type of callable // * @param <V> // * The result type returned by the {@link Future}'s {@link Future#get() get} method // * @param batch // * the collection of tasks to be executed // * @param waitForCompletion // * <code>true</code> if the calling thread should be suspended until all tasks have been executed // * @return a mapping between tasks and their result // * @throws InterruptedException // * @see {@link #submit(Callable)} // */ // public <C extends Callable<V>, V> Map<C, Future<V>> submitBatch(Collection<C> batch, boolean waitForCompletion) // throws InterruptedException { // Map<C, Future<V>> futures = new HashMap<C, Future<V>>(); // if (sDebugSequential) { // for (C c : batch) { // DummyFuture<V> f = new DummyFuture<V>(c); // futures.put(c, f); // f.run(); // } // } else { // for (C c : batch) { // futures.put(c, submit(c)); // } // if (waitForCompletion) { // for (Future<V> f : futures.values()) { // try { // f.get(); // } catch (ExecutionException e) { // // Do nothing // } // } // } // } // return futures; // } // // /** // * Submit a batch of tasks and wait for their termination. // * <p> // * The {@link Future} results should then be checked for exception handling (see {@link Future#get()}) // * </p> // * // * @param batch // * the collection of tasks to be executed // * @param waitForCompletion // * <code>true</code> if the calling thread should be suspended until all tasks have been executed // * @return a mapping between tasks and their result // * @throws InterruptedException // * @see {@link #submit(Runnable)} // */ // public <R extends Runnable> Map<R, Future<?>> executeBatch(Collection<R> batch, boolean waitForCompletion) // throws InterruptedException { // Map<R, Future<?>> futures = new HashMap<R, Future<?>>(batch.size() / 3 * 4); // if (sDebugSequential) { // for (R r : batch) { // DummyFuture<Object> f = new DummyFuture<Object>(r); // futures.put(r, f); // f.run(); // } // } else { // for (R r : batch) { // futures.put(r, submit(r)); // } // // if (waitForCompletion) { // for (Future<?> f : futures.values()) { // try { // f.get(); // } catch (ExecutionException e) { // // Do nothing // } // } // } // } // // return futures; // } public void execute(Runnable command) { // mLock.lock(); // mActiveCount.incrementAndGet(); // mLock.unlock(); // super.execute(command); } /** * Wait for the completion of the current batch of tasks. <br/> * This method assumes that all tasks were added sequentially by the same {@link Thread} that will call this method. * <p> * It is recommended to use {@link #executeBatch(Collection, boolean)} or {@link #submitBatch(Collection, boolean)} * instead * </p> * * @throws InterruptedException */ public void awaitBatchCompletion() throws InterruptedException { mCountDown.await(); } @Override protected void finalize() { mExecutor.shutdown(); } /** * The class <code>WrappedCallable</code> wraps an instance of {@link Callable} and a reference to a * {@link CountDownLatch} that will be updated upon task completion * <p> * Creation date: Apr 23, 2012 - 5:06:41 PM * * @author Victor Pillac, <a href="http://uniandes.edu.co">Universidad de Los Andes</a>-<a * href="http://copa.uniandes.edu.co">Copa</a> <a href="http://www.emn.fr">Ecole des Mines de Nantes</a>-<a * href="http://www.irccyn.ec-nantes.fr/irccyn/d/en/equipes/Slp">SLP</a> * @version 1.0 * @param <V> */ protected static class WrappedCallable<V> implements Callable<V> { private final CountDownLatch mLatch; private final Callable<V> mTask; /** * Creates a new <code>WrappedCallable</code> * * @param latch * @param task */ public WrappedCallable(CountDownLatch latch, Callable<V> task) { mLatch = latch; mTask = task; } @Override public V call() throws Exception { V r = mTask.call(); mLatch.countDown(); return r; } } /** * <code>NameThreadFactory</code> is a simple implementation of {@link ThreadFactory} with custom thread names * <p> * Creation date: May 19, 2011 - 2:07:26 PM * * @author Victor Pillac, <a href="http://uniandes.edu.co">Universidad de Los Andes</a>-<a * href="http://copa.uniandes.edu.co">Copa</a> <a href="http://www.emn.fr">Ecole des Mines de Nantes</a>-<a * href="http://www.irccyn.ec-nantes.fr/irccyn/d/en/equipes/Slp">SLP</a> * @version 1.0 */ public static class NameThreadFactory implements ThreadFactory { static final AtomicInteger sPoolNumber = new AtomicInteger(1); private final ThreadGroup mGroup; private final AtomicInteger mThreadNumber = new AtomicInteger(1); private final String mNamePrefix; /** * Creates a new <code>NameThreadFactory</code> * * @param name * the name of the pool */ public NameThreadFactory(String name) { SecurityManager s = System.getSecurityManager(); mGroup = (s != null) ? s.getThreadGroup() : Thread.currentThread().getThreadGroup(); mNamePrefix = name + "-"; } @Override public Thread newThread(Runnable r) { Thread t = new Thread(getGroup(), r, getNamePrefix() + getThreadNumber().getAndIncrement(), 0); if (t.isDaemon()) t.setDaemon(false); if (t.getPriority() != Thread.NORM_PRIORITY) t.setPriority(Thread.NORM_PRIORITY); return t; } /** * Getter for <code>group</code> * * @return the group */ public ThreadGroup getGroup() { return mGroup; } /** * Getter for <code>namePrefix</code> * * @return the namePrefix */ public String getNamePrefix() { return mNamePrefix; } /** * Getter for <code>threadNumber</code> * * @return the threadNumber */ public AtomicInteger getThreadNumber() { return mThreadNumber; } } /** * <code>DummyFuture</code> is an implementation of {@link Future} for tasks that are executed in a synchronous * manner. * <p> * Creation date: Jun 21, 2011 - 11:49:45 AM * * @author Victor Pillac, <a href="http://uniandes.edu.co">Universidad de Los Andes</a>-<a * href="http://copa.uniandes.edu.co">Copa</a> <a href="http://www.emn.fr">Ecole des Mines de Nantes</a>-<a * href="http://www.irccyn.ec-nantes.fr/irccyn/d/en/equipes/Slp">SLP</a> * @version 1.0 * @param <F> */ protected static class DummyFuture<F> implements Future<F>, Runnable { private F mResult = null; private final Runnable mRun; private final Callable<F> mCall; private ExecutionException mException; protected DummyFuture(Runnable run) { mRun = run; mCall = null; } protected DummyFuture(Callable<F> call) { mRun = null; mCall = call; } @Override public void run() { try { if (mCall != null) mResult = mCall.call(); else mRun.run(); } catch (Exception e) { mException = new ExecutionException(e); } } @Override public boolean cancel(boolean mayInterruptIfRunning) { return false; } @Override public boolean isCancelled() { return false; } @Override public boolean isDone() { return true; } @Override public F get() throws InterruptedException, ExecutionException { if (mException != null) throw mException; return mResult; } @Override public F get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { if (mException != null) throw mException; return mResult; } } }