AIR.Common.Threading.BoundedThreadPool.java Source code

Java tutorial

Introduction

Here is the source code for AIR.Common.Threading.BoundedThreadPool.java

Source

/*******************************************************************************
 * Educational Online Test Delivery System 
 * Copyright (c) 2014 American Institutes for Research
 *   
 * Distributed under the AIR Open Source License, Version 1.0 
 * See accompanying file AIR-License-1_0.txt or at
 * http://www.smarterapp.org/documents/American_Institutes_for_Research_Open_Source_Software_License.pdf
 ******************************************************************************/
package AIR.Common.Threading;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class BoundedThreadPool implements IThreadPool {

    private static int _numberOfProcessors = Runtime.getRuntime().availableProcessors();
    private static final Logger _logger = LoggerFactory.getLogger(BoundedThreadPool.class);

    private final BlockingQueue<Runnable> _taskQueue;
    private final ThreadPoolExecutor _workerThreadPool;
    private final int _taskQHighWaterMark;
    private final int _taskQLowWaterMark;
    private final IThreadPoolStatsRecorder _statsRecorder;
    private final String _name;
    private final Object _statusLock = new Object();

    private volatile boolean _suspendQUntilLowWaterMarkisReached = false;
    private volatile ThreadPoolStatus _status = ThreadPoolStatus.Uninitialized;
    private volatile int _lastThreadNumber = 0;
    private volatile IThreadPoolExceptionHandler _exceptionHandler = null;

    public BoundedThreadPool(int threadCount, String threadPoolName, int highWaterMark, int lowWaterMark) {
        this(threadCount, threadPoolName, highWaterMark, lowWaterMark, true);
    }

    public BoundedThreadPool(int threadCount, String threadPoolName, int highWaterMark, int lowWaterMark,
            boolean captureStats) {

        this(threadCount, threadPoolName, highWaterMark, lowWaterMark,
                captureStats ? new ThreadPoolStatsRecorder() : null);

    }

    public BoundedThreadPool(int threadCount, String threadPoolName, int highWaterMark, int lowWaterMark,
            IThreadPoolStatsRecorder statsRecorder) {
        if (threadCount <= 0) {
            threadCount = Math.max(_numberOfProcessors, 2);
        }
        _name = StringUtils.defaultString(threadPoolName);
        _taskQHighWaterMark = highWaterMark <= 0 ? Integer.MAX_VALUE : highWaterMark;
        _taskQLowWaterMark = lowWaterMark <= 0 ? highWaterMark : lowWaterMark;
        if (lowWaterMark > highWaterMark) {
            throw new IllegalArgumentException("The low watermark cannot be larger than the high watermark");
        }

        if (statsRecorder != null) {
            statsRecorder.setThreadPool(this);
        }
        _statsRecorder = statsRecorder;
        _taskQueue = new ArrayBlockingQueue<>(_taskQHighWaterMark, true);
        ThreadFactory threadFactory = new NamedThreadFactory();
        _workerThreadPool = new ThreadPoolExecutor(threadCount, threadCount, 0, TimeUnit.NANOSECONDS, _taskQueue,
                threadFactory);
        synchronized (_statusLock) {
            _workerThreadPool.prestartAllCoreThreads();
            _status = ThreadPoolStatus.Active;
        }
    }

    public static int getNumberOfProcessors() {
        return _numberOfProcessors;
    }

    public int getCount() {
        return _taskQueue.size();
    }

    public int getThreadCount() {
        return _workerThreadPool.getPoolSize();
    }

    public int getActiveCount() {
        return _workerThreadPool.getActiveCount();
    }

    public IThreadPoolExceptionHandler getExceptionHandler() {
        return _exceptionHandler;
    }

    public void setExceptionHandler(IThreadPoolExceptionHandler value) {
        _exceptionHandler = value;
    }

    public boolean isShutdown() {
        synchronized (_statusLock) {
            return (_status == ThreadPoolStatus.ShuttingDown || _status == ThreadPoolStatus.Closed);
        }
    }

    @Override
    public synchronized boolean Enqueue(ITask task) {

        // We aren't worrying about synchronizing the status read, because reads of
        // volatile properties are atomic by contract
        if (_status != ThreadPoolStatus.Active) {
            return false;
        }

        if (_suspendQUntilLowWaterMarkisReached) {
            if (getCount() >= _taskQLowWaterMark) {
                return false;
            }
            _suspendQUntilLowWaterMarkisReached = false;
        }
        if (_statsRecorder != null) {
            _statsRecorder.recordTaskQueued(task);
        }
        TaskRunner runner = new TaskRunner(task);
        try {
            _workerThreadPool.submit(runner);
        } catch (RejectedExecutionException e) {
            _suspendQUntilLowWaterMarkisReached = true;
            if (_statsRecorder != null) {
                _statsRecorder.recordTaskRejected(task);
            }
            return false;
        }
        return true;
    }

    @Override
    public IThreadPoolStats getStats() {
        if (_statsRecorder != null) {
            return _statsRecorder.getStats();
        }
        return null;
    }

    public void shutdown(boolean wait) {
        synchronized (_statusLock) {
            if (this._status != ThreadPoolStatus.Active)
                return;
            _status = ThreadPoolStatus.ShuttingDown;
        }
        _logger.info(String.format(String.format("BoundedThreadPool \"%s\" Shutting Down", _name)));

        if (wait) {
            _workerThreadPool.shutdown();
        } else {
            _workerThreadPool.shutdownNow();
        }

        // There _really_ should not be anything else that can be changing the
        // status variable at this point, so
        // we don't bother synchronizing.
        _status = ThreadPoolStatus.Closed;
        _logger.info(String.format(String.format("BoundedThreadPool \"%s\" Shut Down", _name)));
    }

    public void finalize() {
        shutdown(false);
    }

    private class TaskRunner implements Runnable {

        private final ITask _task;

        public TaskRunner(ITask task) {
            _task = task;
        }

        @Override
        public void run() {
            if (_statsRecorder != null) {
                _statsRecorder.recordTaskDequeued(_task);
            }
            try {
                _task.Execute();
            } catch (Throwable t) {
                if (_exceptionHandler != null) {
                    try {
                        _exceptionHandler.onException(_task, t);
                    } catch (Throwable t2) {
                        _logger.error("Error was thrown in worker thread's registered exception handler", t2);
                        _logger.error(String.format(
                                "A worker thread's registered exception handler failed while handling the following error\n    \"%s\"",
                                t.getMessage()), t);
                    }
                }
            } finally {
                if (_statsRecorder != null) {
                    _statsRecorder.recordTaskExecuted(_task);
                }
            }
        }

    }

    private class NamedThreadFactory implements ThreadFactory {

        @Override
        public Thread newThread(Runnable r) {
            Thread thread = new Thread(r);
            thread.setDaemon(true);
            String thread_name = "";
            if (!StringUtils.isEmpty(_name)) {
                thread_name = _name + "#" + Integer.toString(_lastThreadNumber);
                _lastThreadNumber++;
            }

            thread.setName(thread_name);

            return thread;
        }

    }

}