/******************************************************************************* * Australian National University Data Commons * Copyright (C) 2013 The Australian National University * * This file is part of Australian National University Data Commons. * * Australian National University Data Commons is free software: you * can redistribute it and/or modify it under the terms of the GNU * General Public License as published by the Free Software Foundation, * either version 3 of the License, or (at your option) any later * version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. ******************************************************************************/ package au.edu.anu.datacommons.tasks; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Future; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.SynchronousQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import javax.annotation.PreDestroy; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; /** * A service class that manages task scheduling. Tasks can be submitted to one of the following pools: * <p> * <ul> * <li>Fixed-size thread pool for most tasks (priority 4) * <li>Unbounded thread pool (priority 3) for tasks that wait for other tasks to complete. * <li>Fixed-size thread pool for idle tasks (priority 1) * </ul> * * @author Rahul Khanna * */ @Component public class ThreadPoolService implements AutoCloseable { private static final Logger LOGGER = LoggerFactory.getLogger(ThreadPoolService.class); private Set<ExecutorService> execs = new HashSet<>(); private ScheduledThreadPoolExecutor scheduledThreadPool; private ThreadPoolExecutor cachedThreadPool; private ScheduledThreadPoolExecutor idleThreadPool; private List<Runnable> cleanupTasks = new ArrayList<Runnable>(); /** * Creates an instance of the the Thread Pool Service. * * @param nThreads */ public ThreadPoolService(int nThreads) { scheduledThreadPool = new ScheduledThreadPoolExecutor(nThreads, new ThreadPoolFactory("fixed", Thread.NORM_PRIORITY - 1)); execs.add(scheduledThreadPool); cachedThreadPool = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>(), new ThreadPoolFactory("cache", Thread.NORM_PRIORITY - 2)); execs.add(cachedThreadPool); idleThreadPool = new ScheduledThreadPoolExecutor(nThreads, new ThreadPoolFactory("idle", Thread.MIN_PRIORITY)); execs.add(idleThreadPool); } /** * @see ScheduledThreadPoolExecutor#submit(Callable) */ public <T> Future<T> submit(Callable<T> task) { return scheduledThreadPool.submit(task); } /** * @see ScheduledThreadPoolExecutor#submit(Runnable) */ public Future<?> submit(Runnable task) { return scheduledThreadPool.submit(task); } /** * @see ScheduledThreadPoolExecutor#scheduleWithFixedDelay(Runnable, long, long, TimeUnit) */ public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit) { return scheduledThreadPool.scheduleWithFixedDelay(command, initialDelay, delay, unit); } /** * @see ScheduledThreadPoolExecutor#submit(Callable) */ public <T> Future<T> submitCachedPool(Callable<T> task) { return cachedThreadPool.submit(task); } /** * @see ThreadPoolExecutor#submit(Callable) */ public <T> Future<T> submitIdlePool(Callable<T> task) { return idleThreadPool.submit(task); } /** * @see ScheduledThreadPoolExecutor#scheduleWithFixedDelay(Runnable, long, long, TimeUnit) */ public ScheduledFuture<?> scheduleWhenIdleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit) { return idleThreadPool.scheduleWithFixedDelay(command, initialDelay, delay, unit); } /** * Adds a cleaup task to the list of tasks that will be performed on the shutdown of this instance. * * @param task * Cleanup task as Runnable */ public synchronized void addCleanupTask(Runnable task) { this.cleanupTasks.add(task); } /** * Waits until all tasks in the thread pool finish. Then performs clean up tasks. */ @Override @PreDestroy public void close() throws Exception { LOGGER.info("Thread Pool Service Shutdown: WAITING..."); for (ExecutorService es : execs) { es.shutdown(); } for (ExecutorService es : execs) { es.awaitTermination(10, TimeUnit.MINUTES); } LOGGER.info("Thread Pool Service Shutdown: SUCCESS."); LOGGER.info("Cleanup tasks: RUNNING..."); for (Runnable task : cleanupTasks) { try { task.run(); } catch (Exception e) { // Error in cleanup tasks cannot be handled by the application. LOGGER.error(e.getMessage(), e); } } LOGGER.info("Cleanup tasks: FINISHED."); } }