All Downloads are FREE. Search and download functionalities are using the official Maven repository.

host.anzo.commons.threading.ThreadPool Maven / Gradle / Ivy

package host.anzo.commons.threading;

import de.mxro.metrics.jre.Metrics;
import delight.async.properties.PropertyNode;
import host.anzo.commons.emergency.metric.IMetric;
import host.anzo.commons.emergency.metric.Metric;
import host.anzo.commons.emergency.metric.MetricGroupType;
import host.anzo.commons.emergency.metric.MetricResult;
import host.anzo.core.config.EmergencyConfig;
import host.anzo.core.startup.EShutdownPriority;
import host.anzo.core.startup.IShutdownable;
import host.anzo.core.startup.StartupComponent;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.text.TextStringBuilder;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * @author ANZO
 * @since 8/19/2021
 */
@Slf4j
@Metric
@StartupComponent(value = "Threading", shutdownPriority = EShutdownPriority.MINOR)
public final class ThreadPool implements IShutdownable, IMetric {
	@Getter(lazy = true)
	private static final ThreadPool instance = new ThreadPool();

	private @Getter final ScheduledThreadPoolExecutor generalThreadPool;
	private final ScheduledFuture purgeTask;

	private final AtomicBoolean shutdowning = new AtomicBoolean(false);

	private final static PropertyNode scheduleMetrics = Metrics.create();
	private final static PropertyNode executeMetrics = Metrics.create();

	private ThreadPool() {
		generalThreadPool = new ScheduledThreadPoolExecutor(Math.max(1, Runtime.getRuntime().availableProcessors() / 2), new ThreadPoolPriorityFactory("GeneralSTPool", Thread.NORM_PRIORITY));
		generalThreadPool.allowCoreThreadTimeOut(false);
		generalThreadPool.setRemoveOnCancelPolicy(true);
		generalThreadPool.setExecuteExistingDelayedTasksAfterShutdownPolicy(false);
		purgeTask = scheduleGeneralAtFixedRate("ThreadPool.PurgeTask()", new PurgeTask(), 5, 5, TimeUnit.MINUTES);

		log.info("ThreadPool initialized:");
		log.info("- GeneralPoolCoreSize: {}/{}", generalThreadPool.getCorePoolSize(), generalThreadPool.getMaximumPoolSize());
	}

	/**
	 * Schedules a general task to be executed after the given delay.
	 * @param name task name
	 * @param task  the task to execute
	 * @param delay the delay in the given time unit
	 * @param unit  the time unit of the delay parameter
	 * @return a ScheduledFuture representing pending completion of the task, and whose get() method will throw an exception upon cancellation
	 */
	public @Nullable ScheduledFuture scheduleGeneral(String name, Runnable task, long delay, TimeUnit unit) {
		if (shutdowning.get()) {
			return null;
		}
		try {
			if (EmergencyConfig.ENABLE_METRICS) {
				scheduleMetrics.record(Metrics.happened(name));
			}
			return generalThreadPool.schedule(new RunnableWrapper(task), delay, unit);
		} catch (RejectedExecutionException ignored) {
			return null;
		}
	}

	public @Nullable ScheduledFuture scheduleGeneral(String name, Runnable task, @NotNull LocalDateTime dateTime) {
		long deadLine = dateTime.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
		long delay = deadLine - System.currentTimeMillis();
		if (delay > 0) {
			if (EmergencyConfig.ENABLE_METRICS) {
				scheduleMetrics.record(Metrics.happened(name));
			}
			return scheduleGeneral(name, task, delay, TimeUnit.MILLISECONDS);
		}
		return null;
	}

	/**
	 * Schedules a general task to be executed at fixed rate.
	 *
	 * @param name         task name
	 * @param task         the task to execute
	 * @param initialDelay the initial delay in the given time unit
	 * @param period       the period between executions in the given time unit
	 * @param unit         the time unit of the initialDelay and period parameters
	 * @return a ScheduledFuture representing pending completion of the task, and whose get() method will throw an exception upon cancellation
	 */
	public @Nullable ScheduledFuture scheduleGeneralAtFixedRate(String name, Runnable task, long initialDelay, long period, TimeUnit unit) {
		if (shutdowning.get()) {
			return null;
		}
		try {
			if (EmergencyConfig.ENABLE_METRICS) {
				scheduleMetrics.record(Metrics.happened(name));
			}
			return generalThreadPool.scheduleAtFixedRate(new RunnableWrapper(task), initialDelay, period, unit);
		} catch (RejectedExecutionException ignored) {
			return null;
		}
	}

	/**
	 * Executes an general task sometime in future in another thread.
	 * @param name task name
	 * @param task the task to execute
	 */
	public void executeGeneral(String name, Runnable task) {
		if (shutdowning.get()) {
			return;
		}
		try {
			if (EmergencyConfig.ENABLE_METRICS) {
				executeMetrics.record(Metrics.happened(name));
			}
			generalThreadPool.execute(new RunnableWrapper(task));
		} catch (RejectedExecutionException ignored) {
		}
	}

	public @NotNull String getStats() {
		final TextStringBuilder builder = new TextStringBuilder();
		builder.appendln(" | -------");
		builder.appendln(" + General:");
		builder.appendln(" |- ActiveThreads:   " + generalThreadPool.getActiveCount());
		builder.appendln(" |- getCorePoolSize: " + generalThreadPool.getCorePoolSize());
		builder.appendln(" |- PoolSize:        " + generalThreadPool.getPoolSize());
		builder.appendln(" |- MaximumPoolSize: " + generalThreadPool.getMaximumPoolSize());
		builder.appendln(" |- CompletedTasks:  " + generalThreadPool.getCompletedTaskCount());
		builder.appendln(" |- ScheduledTasks:  " + generalThreadPool.getQueue().size());
		builder.appendln(" | -------");
		return builder.toString();
	}

	@Override
	public void onShutdown() {
		if (shutdowning.compareAndSet(false, true)) {
			try {
				purgeTask.cancel(false);
				generalThreadPool.shutdown();
				generalThreadPool.purge();
				if (!generalThreadPool.awaitTermination(120, TimeUnit.SECONDS)) {
					generalThreadPool.shutdownNow();
				}
				log.info("All ThreadPools are now stopped");
			} catch (InterruptedException e) {
				log.warn("There has been a problem shutting down the thread pool manager!", e);
			}
		}
	}

	@Override
	public @NotNull List getMetric() {
		final List metricResults = new ArrayList<>();

		final MetricResult scheduleResult = new MetricResult();
		scheduleResult.setMetricGroupType(MetricGroupType.THREADPOOL);
		scheduleResult.setName("Schedule");
		scheduleResult.setData(scheduleMetrics.render().get());
		metricResults.add(scheduleResult);

		final MetricResult executeResult = new MetricResult();
		executeResult.setMetricGroupType(MetricGroupType.THREADPOOL);
		executeResult.setName("Execute");
		executeResult.setData(executeMetrics.render().get());
		metricResults.add(executeResult);
		return metricResults;
	}

	private class PurgeTask implements Runnable {
		@Override
		public void run() {
			generalThreadPool.purge();
		}
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy