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

net.algart.arrays.DefaultThreadPoolFactory Maven / Gradle / Ivy

Go to download

Open-source Java libraries, supporting generalized smart arrays and matrices with elements of any types, including a wide set of 2D-, 3D- and multidimensional image processing and other algorithms, working with arrays and matrices.

The newest version!
/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2007-2024 Daniel Alievsky, AlgART Laboratory (http://algart.net)
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

package net.algart.arrays;

import java.util.Objects;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 

A simple implementation of {@link ThreadPoolFactory} interface. * It uses a thread pool, created by Executors.newFixedThreadPool method, * and uses {@link Arrays.SystemSettings#cpuCount()} method to determine * the desired number of parallel tasks, if you did not specify another number in * numberOfTasks argument of the {@link #DefaultThreadPoolFactory constructor} * or {@link #getDefaultThreadPoolFactory(int)} method. * See details below in comments to the methods and fields. * *

This class is immutable and thread-safe: * there are no ways to modify settings of the created instance.

* * @author Daniel Alievsky */ public class DefaultThreadPoolFactory extends AbstractThreadPoolFactory implements ThreadPoolFactory { private static final long MIN_MULTITHREADING_LENGTH = InternalUtils.getLongProperty( "net.algart.arrays.minMultithreadingLength", 16); private static final int GLOBAL_THREAD_POOLS_PER_CPU = Math.min(128, Math.max(1, InternalUtils.getIntProperty( "net.algart.arrays.globalThreadPoolsPerCPU", 2))); private static final int GLOBAL_THREAD_POOL_SIZE = Math.min(8192, InternalUtils.getIntProperty( "net.algart.arrays.globalThreadPoolSize", 1 + GLOBAL_THREAD_POOLS_PER_CPU * InternalUtils.availableProcessors())); private static final int GLOBAL_THREAD_POOL_KEEP_ALIVE_TIME = Math.max(0, InternalUtils.getIntProperty( "net.algart.arrays.globalThreadPoolKeepAliveTime", 30000)); // 30 sec keep-alive for core threads /** * Returns the global thread pool, returned by {@link #getThreadPool(Array, ThreadFactory)} method * in factories, created by {@link #getDefaultThreadPoolFactory()} * and {@link #getDefaultThreadPoolFactory(int)} methods. * *

More precisely, if there is the system property "net.algart.arrays.globalThreadPoolSize", * containing a positive integer value N, this method returns a global thread pool, * created by Executors.newFixedThreadPool(N,...(someOurFactory)) (or analogous) operator * while the first call of this method and saved in an internal static field. * If this property contains an integer greater than 8192, this value is truncated to 8192. * If this property contains 0 or a negative value, this method returns {@code null} * (in this case, no global thread pool will be created). * If there is no such property, or if it contains not a number, * or if some exception occurred while calling Integer.getInteger, * this method returns the default value, which is equal (in the current implementation) to * {@link Arrays.SystemSettings#availableProcessors()}*MULT+1, * where MULT is an integer value, stored in "net.algart.arrays.globalThreadPoolsPerCPU" * system property, or default multiplier 2 if there is no such property or it contains not a number. * Default value MULT=2 provides a suitable choice for most multiprocessor configurations. * The value of these system properties are loaded and checked only once * while initializing {@link DefaultThreadPoolFactory} class. * *

Note: the default value {@link Arrays.SystemSettings#availableProcessors()}*MULT+1 * can be changed in future implementations. It is only guaranteed that this value is chosen * not less than {@link Arrays.SystemSettings#availableProcessors()}. * *

Note: the threads, created in the global thread pool (if it exists), are daemons. * So, the application can be terminated by the usual way, even * if the global thread pool contains some working threads. * * @return the global thread pool, usually returned by this factory, if it exists, or {@code null} in another case. */ public static ThreadPoolExecutor globalThreadPool() { return ConstantHolder.GLOBAL_THREAD_POOL; } private final int numberOfTasks; private final ThreadPoolExecutor persistentThreadPool; /** * Returns an instance of this class with the default (simplest) behavior. * Namely, it is equivalent to {@link #getDefaultThreadPoolFactory(int) * getDefaultThreadPoolFactory(0)}. This method is the typical way for getting instances of this class. * *

Note: this method works very quickly (it just returns a global internal constant). * * @return an instance of this class with the default behavior. */ public static DefaultThreadPoolFactory getDefaultThreadPoolFactory() { return ConstantHolder.DEFAULT; } /** * Returns an instance of this class with the specified recommended number of tasks. * *

If numberOfTasks argument is positive, * it will be always returned by {@link #recommendedNumberOfTasks()} and * {@link #recommendedNumberOfTasks(Array)} methods. * If it is zero, that method will use the common algorithm, based on the system property: * see comments to {@link #recommendedNumberOfTasks()}. * *

This method is equivalent to new {@link #DefaultThreadPoolFactory(int, ThreadPoolExecutor) * DefaultThreadPoolFactory}(numberOfTasks, {@link #globalThreadPool()}). * *

Note: this method works very quickly (it just returns global internal constants) in cases * numberOfTasks=0 and numberOfTasks=1. * * @param numberOfTasks the desired number of tasks. * @return an instance of this class with the specified recommended number of tasks. * @throws IllegalArgumentException if numberOfTasks is negative. */ public static DefaultThreadPoolFactory getDefaultThreadPoolFactory(int numberOfTasks) { if (numberOfTasks == 0) { return ConstantHolder.DEFAULT; } if (numberOfTasks == 1) { return ConstantHolder.DEFAULT_SINGLE_THREAD; } return new DefaultThreadPoolFactory(numberOfTasks, ConstantHolder.GLOBAL_THREAD_POOL); } /** * Creates new instance of this class with the specified recommended number of tasks * and the specified thread pool. * *

If numberOfTasks argument is positive, * it will be always returned by {@link #recommendedNumberOfTasks()} and * {@link #recommendedNumberOfTasks(Array)} methods. * If it is zero, that method will use the common algorithm, based on the system property: * see comments to {@link #recommendedNumberOfTasks()}. * *

If persistentThreadPool is not {@code null}, * it will be always returned by {@link #getThreadPool(Array, ThreadFactory)} method and * {@link #releaseThreadPool(ExecutorService)} method will do nothing. * In this case, please note, that if the threads, created by this pool, are not daemons, * then the application will probably not be exited until you will directly call shutdown() * method for the passed persistentThreadPool. * *

If persistentThreadPool==null, {@link #getThreadPool(Array, ThreadFactory)} method * will always create new thread pool and * {@link #releaseThreadPool(ExecutorService)} method will shutdown the passed pool. * * @param numberOfTasks the desired number of tasks. * @param persistentThreadPool the desired thread pool, * or {@code null} if {@link #getThreadPool(Array, ThreadFactory)} * should create new thread pool every time. * @throws IllegalArgumentException if numberOfTasks is negative. */ public DefaultThreadPoolFactory(int numberOfTasks, ThreadPoolExecutor persistentThreadPool) { if (numberOfTasks < 0) { throw new IllegalArgumentException("Negative numberOfTasks=" + numberOfTasks); } this.numberOfTasks = numberOfTasks; this.persistentThreadPool = persistentThreadPool; } /** * If this instance was created by the {@link #DefaultThreadPoolFactory constructor} * with numberOfTasks=0 argument * (or via {@link #getDefaultThreadPoolFactory()} method), this implementation returns * the result of {@link Arrays.SystemSettings#cpuCount()} method. * *

If this instance was created by the {@link #DefaultThreadPoolFactory constructor} * with non-zero numberOfTasks argument * (or via {@link #getDefaultThreadPoolFactory(int numberOfTask)} method * with numberOfTasks>0 method), * the given numberOfTasks argument is returned always * regardless of any system properties. * *

If this instance uses persistent thread pool, that is if * {@link #persistentThreadPool()}!=null (a typical situation), * then the result of this method, calculated by the rules above, * is also truncated by the limit * Math.max(1, ((ThreadPoolExecutor){@link #persistentThreadPool()}).getCorePoolSize()) * — if the previously calculated result is greater then this limit, then this limit is returned instead. * So, there is a guarantee, that this method never returns a value greater than * the core number of threads in a thread pool, returned by * {@link #getThreadPool(ThreadFactory)} and {@link #getThreadPool(Array, ThreadFactory)} methods. * It is important if you are going to run threads which can depend on each other. * * @return the recommended number of parallel tasks to perform the processing. * @see #recommendedNumberOfTasks(Array) */ public int recommendedNumberOfTasks() { int nt = numberOfTasks != 0 ? numberOfTasks : Arrays.SystemSettings.cpuCount(); if (nt > 1 && persistentThreadPool != null) { int corePoolSize = Math.max(1, persistentThreadPool.getCorePoolSize()); if (corePoolSize < nt) { nt = corePoolSize; } } return nt; } /** * This method returns the same value as {@link #recommendedNumberOfTasks()}, excepting that * when the passed array is very little, this method may return less value (usually 1). * *

However, if this instance was created by the {@link #DefaultThreadPoolFactory constructor} * with non-zero numberOfTasks argument * (or via {@link #getDefaultThreadPoolFactory(int numberOfTask)} method * with numberOfTasks>0 method), * then this method is strictly equivalent to {@link #recommendedNumberOfTasks()}. * * @param sourceArray some AlgART array that should be processed. * @return the recommended number of parallel tasks to perform the processing. * @throws NullPointerException if the argument is {@code null}. */ public int recommendedNumberOfTasks(Array sourceArray) { Objects.requireNonNull(sourceArray, "Null sourceArray argument"); if (numberOfTasks != 0) { return recommendedNumberOfTasks(); } long len = sourceArray.length(); if (len < MIN_MULTITHREADING_LENGTH) { return 1; } else { return (int) Math.min(len, recommendedNumberOfTasks()); // no sense to create more threads than the number of elements } } public ThreadPoolFactory singleThreadVersion() { if (this.numberOfTasks == 1) { return this; } return new DefaultThreadPoolFactory(1, persistentThreadPool); } /** * This implementation returns the {@link #persistentThreadPool() persistent thread pool}, * if it exists, or creates new thread pool by the following call: * Executors.newFixedThreadPool({@link #recommendedNumberOfTasks()}, * threadFactory==null ? Executors.defaultThreadFactory() : threadFactory). * * @param threadFactory if not {@code null} and there is no * {@link #persistentThreadPool() persistent thread pool}, * specifies the desired thread factory for using by new thread pool. * @return the thread pool for parallel processing the array. * @throws NullPointerException if sourceArray argument is {@code null}. * @see #getThreadPool(Array, ThreadFactory) */ public ExecutorService getThreadPool(ThreadFactory threadFactory) { return persistentThreadPool != null ? persistentThreadPool : Executors.newFixedThreadPool(recommendedNumberOfTasks(), threadFactory == null ? Executors.defaultThreadFactory() : threadFactory); } /** * This implementation returns the {@link #persistentThreadPool() persistent thread pool}, * if it exists, or creates new thread pool by the following call: * Executors.newFixedThreadPool({@link #recommendedNumberOfTasks(Array) * recommendedNumberOfTasks}(sourceArray), * threadFactory==null ? Executors.defaultThreadFactory() : threadFactory). * * @param sourceArray some AlgART array that should be processed. * @param threadFactory if not {@code null} and there is no * {@link #persistentThreadPool() persistent thread pool}, * specifies the desired thread factory for using by new thread pool. * @return the thread pool for parallel processing the array. * @throws NullPointerException if sourceArray argument is {@code null}. */ public ExecutorService getThreadPool(Array sourceArray, ThreadFactory threadFactory) { Objects.requireNonNull(sourceArray, "Null sourceArray argument"); return persistentThreadPool != null ? persistentThreadPool : Executors.newFixedThreadPool(recommendedNumberOfTasks(sourceArray), threadFactory == null ? Executors.defaultThreadFactory() : threadFactory); } /** * This implementation calls pool.shutdown(), if there is no persistent thread pool * ({@link #persistentThreadPool()} returns {@code null}), or does nothing in another case. * * @param pool the thread pool created by the previous {@link #getThreadPool(Array, ThreadFactory)} call. * @throws NullPointerException if poll argument is {@code null} * and there is no persistent thread pool. */ public void releaseThreadPool(ExecutorService pool) { if (persistentThreadPool == null) { pool.shutdown(); } } /** * Returns the persistent thread pool, * returned by all calls of {@link #getThreadPool(Array, ThreadFactory)} method, * if it exists, or {@code null} in another case. * (In the second case, every call of {@link #getThreadPool(Array, ThreadFactory)} method * creates new thread pool.) * *

More precisely, if this instance was created * by the {@link #DefaultThreadPoolFactory(int, ThreadPoolExecutor) constructor}, * this method returns its 2nd argument. * If this instance was created * by {@link #getDefaultThreadPoolFactory()} * or {@link #getDefaultThreadPoolFactory(int)}, * this method returns the result of {@link #globalThreadPool()}. * * @return the persistent thread pool and {@code null} if it exists, or {@code null} in another case. */ public final ExecutorService persistentThreadPool() { return persistentThreadPool; } private static class ConstantHolder { private static final ThreadPoolExecutor GLOBAL_THREAD_POOL = GLOBAL_THREAD_POOL_SIZE <= 0 ? null : new ThreadPoolExecutor(GLOBAL_THREAD_POOL_SIZE, GLOBAL_THREAD_POOL_SIZE, GLOBAL_THREAD_POOL_KEEP_ALIVE_TIME, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>(), new ThreadFactory() { private final AtomicInteger threadNumber = new AtomicInteger(1); public Thread newThread(Runnable r) { Thread t = new Thread(r, "AlgART-arrays-thread-" + threadNumber.getAndIncrement()); t.setDaemon(true); return t; } } ); static { if (GLOBAL_THREAD_POOL != null && GLOBAL_THREAD_POOL_KEEP_ALIVE_TIME > 0) { GLOBAL_THREAD_POOL.allowCoreThreadTimeOut(true); } } private static final DefaultThreadPoolFactory DEFAULT = new DefaultThreadPoolFactory(0, GLOBAL_THREAD_POOL); private static final DefaultThreadPoolFactory DEFAULT_SINGLE_THREAD = new DefaultThreadPoolFactory(1, GLOBAL_THREAD_POOL); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy