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

dev.ikm.tinkar.provider.executor.ExecutorProvider Maven / Gradle / Ivy

There is a newer version: 1.78.0
Show newest version
/*
 * Copyright © 2015 Integrated Knowledge Management ([email protected])
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package dev.ikm.tinkar.provider.executor;

import dev.ikm.tinkar.common.alert.UncaughtExceptionAlertStreamer;
import dev.ikm.tinkar.common.service.ExecutorService;
import dev.ikm.tinkar.common.util.thread.NamedThreadFactory;
import dev.ikm.tinkar.common.util.thread.ThreadPoolExecutorFixed;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.UUID;
import java.util.concurrent.*;

/**
 * Generally available thread pools for doing background processing in an ISAAC application.
 * 

* The {@link #forkJoinThreadPool()} that this provides is identical to the @{link {@link ForkJoinPool#commonPool()} * with the exception that it will bottom out at 6 processing threads, rather than 1, to help prevent * deadlock situations in common ISAAC usage patterns. This has an unbounded queue depth, and LIFO behavior. *

* The {@link #blockingThreadPool()} that this provides is a standard thread pool with (up to) the same number of threads * as there are cores present on the computer - with a minimum of 6 threads. This executor has no queue - internally * it uses a {@link SynchronousQueue} - so if no thread is available to accept the task being queued, it will block * submission of the task until a thread is available to accept the job. *

* The {@link #threadPool()} that this provides is a standard thread pool with (up to) the same number of threads * as there are cores present on the computer - with a minimum of 6 threads. This executor has an unbounded queue * depth, and FIFO behavior. *

* The {@link #ioThreadPool()} that this provides is a standard thread pool with 6 threads. This executor has an unbounded queue * depth, and FIFO behavior. This executor is good for jobs that tend to block on disk IO, where you don't want many running in parallel. * * @author Dan Armbrust */ public class ExecutorProvider implements ExecutorService { private static final Logger LOG = LoggerFactory.getLogger(ExecutorProvider.class); private static final UncaughtExceptionAlertStreamer exceptionAlertStreamer = new UncaughtExceptionAlertStreamer(); /** * The fork join executor. */ private ForkJoinPool forkJoinExecutor; /** * The blocking thread pool executor. */ private ThreadPoolExecutor blockingThreadPoolExecutor; /** * The thread pool executor. */ private ThreadPoolExecutor threadPoolExecutor; /** * The io thread pool executor. */ private TinkarThreadPoolExecutor ioThreadPoolExecutor; /** * The scheduled executor. */ private TinkarScheduledExecutor scheduledExecutor; /** * Start me. */ protected void start() { if (forkJoinExecutor == null) { LOG.info("Starting the WorkExecutors thread pools"); // The java default ForkJoinPool.commmonPool starts with only 1 thread, on 1 and 2 core systems, which can get us deadlocked pretty easily. final int procCount = Runtime.getRuntime() .availableProcessors(); final int parallelism = ((procCount - 1) < 6 ? 6 : procCount - 1); // set between 6 and 1 less than proc count (not less than 6) this.forkJoinExecutor = new ForkJoinPool(parallelism); final int corePoolSize = 2; final int maximumPoolSize = parallelism; final int keepAliveTime = 60; final TimeUnit timeUnit = TimeUnit.SECONDS; // The blocking executor this.blockingThreadPoolExecutor = new ThreadPoolExecutorFixed(corePoolSize, maximumPoolSize, keepAliveTime, timeUnit, new SynchronousQueue<>(), new NamedThreadFactory("Tinkar-B-work-thread", true)); this.blockingThreadPoolExecutor.setRejectedExecutionHandler((runnable, executor) -> { try { executor.getQueue() .offer(runnable, Long.MAX_VALUE, TimeUnit.HOURS); } catch (final Exception e) { throw new RejectedExecutionException("Interrupted while waiting to enqueue"); } }); // The non-blocking executor - set core threads equal to max - otherwise, it will never increase the thread count // with an unbounded queue. this.threadPoolExecutor = new ThreadPoolExecutorFixed(maximumPoolSize, maximumPoolSize, keepAliveTime, timeUnit, new LinkedBlockingQueue<>(), new NamedThreadFactory("Tinkar-Q-work-thread", true)); this.threadPoolExecutor.allowCoreThreadTimeOut(true); // The IO non-blocking executor - set core threads equal to max - otherwise, it will never increase the thread count // with an unbounded queue. this.ioThreadPoolExecutor = new TinkarThreadPoolExecutor(6, 6, keepAliveTime, timeUnit, new LinkedBlockingQueue<>(), new NamedThreadFactory("Tinkar-IO-work-thread", true)); this.ioThreadPoolExecutor.allowCoreThreadTimeOut(true); // Execute this once, early on, in a background thread - as randomUUID uses secure random - and the initial // init of secure random can block on many systems that don't have enough entropy occuring. The DB load process // should provide enough entropy to get it initialized, so it doesn't pause things later when someone requests a random UUID. threadPool().execute(() -> UUID.randomUUID()); this.scheduledExecutor = new TinkarScheduledExecutor(1, new NamedThreadFactory("Tinkar-Scheduled-Thread", true)); LOG.info("WorkExecutors thread pools ready"); } } /** * Stop me. */ protected void stop() { LOG.info("Stopping WorkExecutors thread pools. "); if (this.forkJoinExecutor != null) { this.forkJoinExecutor.shutdownNow(); this.forkJoinExecutor = null; } if (this.blockingThreadPoolExecutor != null) { this.blockingThreadPoolExecutor.shutdownNow(); this.blockingThreadPoolExecutor = null; } if (this.threadPoolExecutor != null) { this.threadPoolExecutor.shutdownNow(); this.threadPoolExecutor = null; } if (this.ioThreadPoolExecutor != null) { this.ioThreadPoolExecutor.shutdownNow(); this.ioThreadPoolExecutor = null; } if (this.scheduledExecutor != null) { this.scheduledExecutor.shutdownNow(); this.scheduledExecutor = null; } LOG.info("Stopped WorkExecutors thread pools"); } //~--- get methods --------------------------------------------------------- /** * Gets the fork join pool executor. * * @return the Tinkar common {@link ForkJoinPool} instance - (behavior described in the class docs) * This is backed by an unbounded queue - it won't block / reject submissions because of being full. */ @Override public ForkJoinPool forkJoinThreadPool() { return this.forkJoinExecutor; } /** * Gets the potentially blocking executor. * * @return The Tinkar common {@link ThreadPoolExecutor} - (behavior described in the class docs). * This is a synchronous queue - if no thread is available to take a job, it will block until a thread * is available to accept the job. */ public ThreadPoolExecutor blockingThreadPool() { return this.blockingThreadPoolExecutor; } /** * Gets the executor. * * @return The Tinkar common {@link ThreadPoolExecutor} - (behavior described in the class docs). * This is backed by an unbounded queue - it won't block / reject submissions because of being full. * This executor has processing threads linked to the number of CPUs available. It is good for compute * intensive jobs. */ @Override public ThreadPoolExecutor threadPool() { return this.threadPoolExecutor; } /** * Gets the IO executor. * * @return The Tinkar common IO {@link ThreadPoolExecutor} - (behavior described in the class docs). * This is backed by an unbounded queue - it won't block / reject submissions because of being full. * This executor differs from {@link #threadPool()} by having a much smaller number of threads - good for * jobs that tend to block on IO. */ @Override public ThreadPoolExecutor ioThreadPool() { return this.ioThreadPoolExecutor; } /** * Gets the scheduled thread pool executor. * * @return the Tinkar common {@link ScheduledThreadPoolExecutor} instance - (behavior described in the class docs) * This pool only has a single thread - submitted jobs should be fast executing. */ @Override public ScheduledExecutorService scheduled() { return this.scheduledExecutor; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy