/** * Copyright (c) 2000-present Liferay, Inc. All rights reserved. * * This library is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 2.1 of the License, or (at your option) * any later version. * * This library 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 Lesser General Public License for more * details. */ package com.liferay.portal.kernel.concurrent; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.concurrent.AbstractExecutorService; import java.util.concurrent.Callable; import java.util.concurrent.Executors; import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.AbstractQueuedSynchronizer; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.ReentrantLock; /** * <p> * See https://issues.liferay.com/browse/LPS-14986. * </p> * * @author Shuyang Zhou */ public class ThreadPoolExecutor extends AbstractExecutorService { public ThreadPoolExecutor(int corePoolSize, int maxPoolSize) { this( corePoolSize, maxPoolSize, 60, TimeUnit.SECONDS, false, Integer.MAX_VALUE, new AbortPolicy(), Executors.defaultThreadFactory(), new ThreadPoolHandlerAdapter()); } public ThreadPoolExecutor( int corePoolSize, int maxPoolSize, long keepAliveTime, TimeUnit timeUnit, boolean allowCoreThreadTimeout, int maxQueueSize) { this( corePoolSize, maxPoolSize, keepAliveTime, timeUnit, allowCoreThreadTimeout, maxQueueSize, new AbortPolicy(), Executors.defaultThreadFactory(), new ThreadPoolHandlerAdapter()); } public ThreadPoolExecutor( int corePoolSize, int maxPoolSize, long keepAliveTime, TimeUnit timeUnit, boolean allowCoreThreadTimeout, int maxQueueSize, RejectedExecutionHandler rejectedExecutionHandler, ThreadFactory threadFactory, ThreadPoolHandler threadPoolHandler) { if ((corePoolSize < 0) || (maxPoolSize <= 0) || (maxPoolSize < corePoolSize) || (keepAliveTime < 0) || (maxQueueSize <= 0)) { throw new IllegalArgumentException(); } if ((rejectedExecutionHandler == null) || (threadFactory == null) || (threadPoolHandler == null)) { throw new NullPointerException(); } _corePoolSize = corePoolSize; _maxPoolSize = maxPoolSize; _keepAliveTime = timeUnit.toNanos(keepAliveTime); _allowCoreThreadTimeout = allowCoreThreadTimeout; _rejectedExecutionHandler = rejectedExecutionHandler; _threadFactory = threadFactory; _threadPoolHandler = threadPoolHandler; _taskQueue = new TaskQueue<>(maxQueueSize); _workerTasks = new HashSet<>(); } public void adjustPoolSize(int newCorePoolSize, int newMaxPoolSize) { if ((newCorePoolSize < 0) || (newMaxPoolSize <= 0) || (newMaxPoolSize < newCorePoolSize)) { throw new IllegalArgumentException(); } _mainLock.lock(); try { int surplusCoreThreads = _corePoolSize - newCorePoolSize; int surplusMaxPoolSize = _maxPoolSize - newMaxPoolSize; _corePoolSize = newCorePoolSize; _maxPoolSize = newMaxPoolSize; if (((surplusCoreThreads > 0) && (_poolSize > _corePoolSize)) || ((surplusMaxPoolSize > 0) && (_poolSize > _maxPoolSize))) { int interruptCount = Math.max( surplusCoreThreads, surplusMaxPoolSize); for (WorkerTask workerTask : _workerTasks) { if (interruptCount > 0) { if (workerTask._interruptIfWaiting()) { interruptCount--; } } else { break; } } } else { Runnable runnable = null; while ((surplusCoreThreads++ < 0) && (_poolSize < _corePoolSize) && ((runnable = _taskQueue.poll()) != null)) { _doAddWorkerThread(runnable); } } } finally { _mainLock.unlock(); } } @Override public boolean awaitTermination(long timeout, TimeUnit timeUnit) throws InterruptedException { long nanos = timeUnit.toNanos(timeout); _mainLock.lock(); try { while (true) { if (_runState == _TERMINATED) { return true; } if (nanos <= 0) { return false; } nanos = _terminationCondition.awaitNanos(nanos); } } finally { _mainLock.unlock(); } } @Override public void execute(Runnable runnable) { if (runnable == null) { throw new NullPointerException(); } boolean[] hasWaiterMarker = new boolean[1]; if ((_runState == _RUNNING) && _taskQueue.offer(runnable, hasWaiterMarker)) { if (_runState != _RUNNING) { if (_taskQueue.remove(runnable)) { _rejectedExecutionHandler.rejectedExecution(runnable, this); } return; } if (!hasWaiterMarker[0]) { _addWorkerThread(); } return; } _rejectedExecutionHandler.rejectedExecution(runnable, this); } public int getActiveCount() { _mainLock.lock(); try { int count = 0; for (WorkerTask workerTask : _workerTasks) { if (workerTask._isLocked()) { count++; } } return count; } finally { _mainLock.unlock(); } } public long getCompletedTaskCount() { _mainLock.lock(); try { long count = _completedTaskCount; for (WorkerTask workerTask : _workerTasks) { count += workerTask._localCompletedTaskCount; } return count; } finally { _mainLock.unlock(); } } public int getCorePoolSize() { return _corePoolSize; } public long getKeepAliveTime(TimeUnit timeUnit) { return timeUnit.convert(_keepAliveTime, TimeUnit.NANOSECONDS); } public int getLargestPoolSize() { return _largestPoolSize; } public int getMaxPoolSize() { return _maxPoolSize; } public String getName() { return _name; } public int getPendingTaskCount() { return _taskQueue.size(); } public int getPoolSize() { return _poolSize; } public RejectedExecutionHandler getRejectedExecutionHandler() { return _rejectedExecutionHandler; } public int getRemainingTaskQueueCapacity() { return _taskQueue.remainingCapacity(); } public long getTaskCount() { _mainLock.lock(); try { long count = _completedTaskCount; for (WorkerTask workerTask : _workerTasks) { count += workerTask._localCompletedTaskCount; if (workerTask._isLocked()) { count++; } } return count + _taskQueue.size(); } finally { _mainLock.unlock(); } } public ThreadFactory getThreadFactory() { return _threadFactory; } public ThreadPoolHandler getThreadPoolHandler() { return _threadPoolHandler; } public boolean isAllowCoreThreadTimeout() { return _allowCoreThreadTimeout; } @Override public boolean isShutdown() { if (_runState != _RUNNING) { return true; } else { return false; } } @Override public boolean isTerminated() { if (_runState == _TERMINATED) { return true; } else { return false; } } public boolean isTerminating() { if (_runState == _STOP) { return true; } else { return false; } } public void setAllowCoreThreadTimeout(boolean allowCoreThreadTimeout) { _allowCoreThreadTimeout = allowCoreThreadTimeout; } public void setKeepAliveTime(long keepAliveTime, TimeUnit timeUnit) { if (keepAliveTime < 0) { throw new IllegalArgumentException(); } _keepAliveTime = timeUnit.toNanos(keepAliveTime); } public void setName(String name) { _name = name; } public void setRejectedExecutionHandler( RejectedExecutionHandler rejectedExecutionHandler) { if (rejectedExecutionHandler == null) { throw new NullPointerException(); } _rejectedExecutionHandler = rejectedExecutionHandler; } public void setThreadFactory(ThreadFactory threadFactory) { if (threadFactory == null) { throw new NullPointerException(); } _threadFactory = threadFactory; } public void setThreadPoolHandler(ThreadPoolHandler threadPoolHandler) { if (threadPoolHandler == null) { throw new NullPointerException(); } _threadPoolHandler = threadPoolHandler; } @Override public void shutdown() { _mainLock.lock(); try { int state = _runState; if (state < _SHUTDOWN) { _runState = _SHUTDOWN; } for (WorkerTask workerTask : _workerTasks) { workerTask._interruptIfWaiting(); } _tryTerminate(); } finally { _mainLock.unlock(); } } @Override public List<Runnable> shutdownNow() { _mainLock.lock(); try { int state = _runState; if (state < _STOP) { _runState = _STOP; } for (WorkerTask workerTask : _workerTasks) { workerTask._thread.interrupt(); } List<Runnable> runnables = new ArrayList<>(); _taskQueue.drainTo(runnables); _tryTerminate(); return runnables; } finally { _mainLock.unlock(); } } @Override public <T> NoticeableFuture<T> submit(Callable<T> callable) { if (callable == null) { throw new NullPointerException("Callable is null"); } DefaultNoticeableFuture<T> defaultNoticeableFuture = newTaskFor( callable); execute(defaultNoticeableFuture); return defaultNoticeableFuture; } @Override public NoticeableFuture<?> submit(Runnable runnable) { return submit(runnable, null); } @Override public <T> NoticeableFuture<T> submit(Runnable runnable, T result) { if (runnable == null) { throw new NullPointerException("Runnable is null"); } DefaultNoticeableFuture<T> defaultNoticeableFuture = newTaskFor( runnable, result); execute(defaultNoticeableFuture); return defaultNoticeableFuture; } public NoticeableFuture<Void> terminationNoticeableFuture() { return _terminationDefaultNoticeableFuture; } @Override protected void finalize() { shutdown(); } protected ReentrantLock getMainLock() { return _mainLock; } protected TaskQueue<Runnable> getTaskQueue() { return _taskQueue; } protected Set<WorkerTask> getWorkerTasks() { return _workerTasks; } @Override protected <T> DefaultNoticeableFuture<T> newTaskFor(Callable<T> callable) { return new DefaultNoticeableFuture<>(callable); } @Override protected <T> DefaultNoticeableFuture<T> newTaskFor( Runnable runnable, T value) { return new DefaultNoticeableFuture<>(runnable, value); } private void _addWorkerThread() { int runState = _runState; int poolSize = _poolSize; if (((runState == _RUNNING) && (poolSize < _maxPoolSize)) || ((runState == _SHUTDOWN) && (poolSize == 0) && !_taskQueue.isEmpty())) { _mainLock.lock(); try { runState = _runState; poolSize = _poolSize; if (((runState == _RUNNING) && (poolSize < _maxPoolSize)) || ((runState == _SHUTDOWN) && (poolSize == 0) && !_taskQueue.isEmpty())) { Runnable runnable = _taskQueue.poll(); if (runnable != null) { _doAddWorkerThread(runnable); } } } finally { _mainLock.unlock(); } } } private void _doAddWorkerThread(Runnable runnable) { WorkerTask workerTask = new WorkerTask(runnable); _workerTasks.add(workerTask); int poolSize = ++_poolSize; if (poolSize > _largestPoolSize) { _largestPoolSize = poolSize; } workerTask._startWork(); } private Runnable _getTask(WorkerTask workerTask, boolean[] cleanUpMarker) { while (true) { try { int state = _runState; if (state >= _STOP) { return null; } Runnable runnable = null; if (state == _SHUTDOWN) { runnable = _taskQueue.poll(); } else if ((_poolSize > _corePoolSize) || _allowCoreThreadTimeout) { runnable = _taskQueue.poll( _keepAliveTime, TimeUnit.NANOSECONDS); } else { runnable = _taskQueue.take(); } if (runnable != null) { return runnable; } _mainLock.lock(); try { if ((_runState >= _STOP) || ((_runState >= _SHUTDOWN) && _taskQueue.isEmpty()) || (_allowCoreThreadTimeout && ((_poolSize > 1) || _taskQueue.isEmpty())) || (!_allowCoreThreadTimeout && (_poolSize > _corePoolSize))) { _completedTaskCount += workerTask._localCompletedTaskCount; _workerTasks.remove(workerTask); if (--_poolSize == 0) { _tryTerminate(); } cleanUpMarker[0] = true; return null; } } finally { _mainLock.unlock(); } } catch (InterruptedException ie) { } } } private void _tryTerminate() { if (_poolSize == 0) { int state = _runState; if ((state == _STOP) || ((state == _SHUTDOWN) && _taskQueue.isEmpty())) { _runState = _TERMINATED; _terminationCondition.signalAll(); _threadPoolHandler.terminated(); _terminationDefaultNoticeableFuture.run(); return; } if (!_taskQueue.isEmpty()) { _doAddWorkerThread(_taskQueue.poll()); } } } private static final int _RUNNING = 0; private static final int _SHUTDOWN = 1; private static final int _STOP = 2; private static final int _TERMINATED = 3; private volatile boolean _allowCoreThreadTimeout; private long _completedTaskCount; private volatile int _corePoolSize; private volatile long _keepAliveTime; private volatile int _largestPoolSize; private final ReentrantLock _mainLock = new ReentrantLock(); private volatile int _maxPoolSize; private String _name; private volatile int _poolSize; private volatile RejectedExecutionHandler _rejectedExecutionHandler; private volatile int _runState; private final TaskQueue<Runnable> _taskQueue; private final Condition _terminationCondition = _mainLock.newCondition(); private final DefaultNoticeableFuture<Void> _terminationDefaultNoticeableFuture = new DefaultNoticeableFuture<Void>() { @Override public boolean cancel(boolean mayInterruptIfRunning) { return false; } }; private volatile ThreadFactory _threadFactory; private volatile ThreadPoolHandler _threadPoolHandler; private final Set<WorkerTask> _workerTasks; private class WorkerTask extends AbstractQueuedSynchronizer implements Runnable { public WorkerTask(Runnable runnable) { _runnable = runnable; } @Override public void run() { boolean[] cleanUpMarker = new boolean[1]; try { Runnable runnable = _runnable; _runnable = null; do { if (runnable != null) { _runTask(runnable); runnable = null; } } while ((runnable = _getTask(this, cleanUpMarker)) != null); } finally { if (!cleanUpMarker[0]) { _mainLock.lock(); try { _completedTaskCount += _localCompletedTaskCount; _workerTasks.remove(this); if (--_poolSize == 0) { _tryTerminate(); } } finally { _mainLock.unlock(); } } _threadPoolHandler.beforeThreadEnd(_thread); } } @Override protected boolean isHeldExclusively() { if (getState() == 1) { return true; } else { return false; } } @Override protected boolean tryAcquire(int unused) { return compareAndSetState(0, 1); } @Override protected boolean tryRelease(int unused) { setState(0); return true; } private boolean _interruptIfWaiting() { if (!_thread.isInterrupted() && tryAcquire(1)) { try { _thread.interrupt(); return true; } finally { _unlock(); } } return false; } private boolean _isLocked() { return isHeldExclusively(); } private void _lock() { acquire(1); } private void _runTask(Runnable task) { _lock(); try { if ((_runState < _STOP) && Thread.interrupted() && (_runState >= _STOP)) { _thread.interrupt(); } Throwable throwable = null; _threadPoolHandler.beforeExecute(_thread, task); try { task.run(); _localCompletedTaskCount++; } catch (RuntimeException re) { throwable = re; throw re; } finally { _threadPoolHandler.afterExecute(task, throwable); } } finally { _unlock(); } } private void _startWork() { _thread = _threadFactory.newThread(this); _threadPoolHandler.beforeThreadStart(_thread); _thread.start(); } private void _unlock() { release(1); } private volatile long _localCompletedTaskCount; private Runnable _runnable; private Thread _thread; } }