using new ThreadPoolUtil to size and use and, where necessary, create thread pool executors so we reduce the amount of runnable threads

Change-Id: I56a9f68aca7b28dc28fbb096f6bfb6cb9dce35a9
This commit is contained in:
Axel Uhl committed 2016-08-15 18:24:18 +02:00
1 parent 687f57caa6
commit 852f114d84
11 files changed
+105 -68

No files matched your search

@@ -13,8 +13,6 @@ import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.locks.ReentrantReadWriteLock;
@@ -25,7 +23,6 @@ import com.sap.sse.common.Util.Pair;
import com.sap.sse.concurrent.LockUtil;
import com.sap.sse.concurrent.NamedReentrantReadWriteLock;
import com.sap.sse.util.SmartFutureCache.UpdateInterval;
import com.sap.sse.util.impl.ThreadFactoryWithPriority;
/**
* A cache for which a background update can be triggered. Readers can decide whether they want to wait for any ongoing
@@ -87,23 +84,13 @@ public class SmartFutureCache<K, V, U extends UpdateInterval<U>> {
private final Map<K, V> cache;
/**
* When many updates are triggered in a short period of time by a single thread, ensure that the single thread
* providing the updates is not outperformed by all the re-calculations happening here. Leave at least one
* core to other things, but by using at least three threads ensure that no simplistic deadlocks may occur.
*/
private static final int THREAD_POOL_SIZE = Math.max(Runtime.getRuntime().availableProcessors()/2, 3);
/**
* Note that this needs to have more than one thread because there may be calculations used for cache updates that
* need to wait for other cache updates to finish. If those were all to be handled by a single thread, deadlocks
* would occur. Remember that there may still be single-core machines, so the factor with which
* <code>availableProcessors</code> is multiplied needs to be greater than one at least.
*/
private final static Executor recalculator = new ThreadPoolExecutor(/* corePoolSize */ THREAD_POOL_SIZE,
/* maximumPoolSize */ THREAD_POOL_SIZE,
/* keepAliveTime */ 60, TimeUnit.SECONDS,
/* workQueue */ new LinkedBlockingQueue<Runnable>(), new ThreadFactoryWithPriority(Thread.NORM_PRIORITY-1, /* daemon */ true));
private final static Executor recalculator = ThreadPoolUtil.INSTANCE.getDefaultBackgroundTaskThreadPoolExecutor();
private final CacheUpdater<K, V, U> cacheUpdateComputer;
@@ -0,0 +1,47 @@
package com.sap.sse.util;
import java.util.concurrent.ExecutorService;
import com.sap.sse.util.impl.ThreadPoolUtilImpl;
public interface ThreadPoolUtil {
ThreadPoolUtil INSTANCE = new ThreadPoolUtilImpl();
/**
* Returns a central, default thread pool executor that can be used to schedule background tasks. Using this
* centralized copy ensures that when several application modules require such an executor they get a single one
* whose dimensions are chosen such that it does not outperform other system-critical tasks such as garbage
* collection and JITting.
* <p>
*
* The executor will have slightly less than {@link Thread#NORM_PRIORITY} as its priority, and its threads will all
* be {@link Thread#setDaemon daemon threads}. The task queue is limited by {@link Integer#MAX_VALUE}, so is
* virtually unbounded, and therefore no specific rejection handler is configured.
* <p>
*
* The thread pool uses constant size, so the core size equals the maximum size, and threads will not be terminated
* once created. Its size is calculated using {@link #getReasonableThreadPoolSize()}.
*/
ExecutorService getDefaultBackgroundTaskThreadPoolExecutor();
/**
* Returns a new thread pool executor that can be used to schedule background tasks. Normally, clients should use
* {@link #getDefaultBackgroundTaskThreadPoolExecutor()} instead. However, under certain rare circumstances it may
* be necessary or advisable to create a separate thread pool, e.g., when the computations of tasks submitted to the
* default thread pool depend on the results of other tasks. Those other tasks then must not be submitted to the same
* executor as deadlocks may occur.<p>
*
* The same configuration as for {@link #getDefaultBackgroundTaskThreadPoolExecutor()} is used.
*/
ExecutorService createBackgroundTaskThreadPoolExecutor();
/**
* In case an application module really requires its own thread pool instead of
* using {@link #getBackgroundTaskThreadPool}, it should size the thread pool
* using the value returned by this method. The number returned will scale with
* the number of {@link Runtime#availableProcessors() CPUs available} and will
* try to leave some room for system threads such as the garbage collector and
* JITter.
*/
int getReasonableThreadPoolSize();
}
@@ -0,0 +1,39 @@
package com.sap.sse.util.impl;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import com.sap.sse.util.ThreadPoolUtil;
public class ThreadPoolUtilImpl implements ThreadPoolUtil {
private static final int REASONABLE_THREAD_POOL_SIZE = Math.max(Runtime.getRuntime().availableProcessors()/2, 3);
private final ExecutorService defaultBackgroundTaskThreadPoolExecutor;
public ThreadPoolUtilImpl() {
defaultBackgroundTaskThreadPoolExecutor = new ThreadPoolExecutor(/* corePoolSize */ REASONABLE_THREAD_POOL_SIZE,
/* maximumPoolSize */ REASONABLE_THREAD_POOL_SIZE,
/* keepAliveTime */ 60, TimeUnit.SECONDS,
/* workQueue */ new LinkedBlockingQueue<Runnable>(), new ThreadFactoryWithPriority(Thread.NORM_PRIORITY-1, /* daemon */ true));
}
@Override
public ExecutorService getDefaultBackgroundTaskThreadPoolExecutor() {
return defaultBackgroundTaskThreadPoolExecutor;
}
@Override
public ExecutorService createBackgroundTaskThreadPoolExecutor() {
return new ThreadPoolExecutor(/* corePoolSize */ REASONABLE_THREAD_POOL_SIZE,
/* maximumPoolSize */ REASONABLE_THREAD_POOL_SIZE,
/* keepAliveTime */ 60, TimeUnit.SECONDS,
/* workQueue */ new LinkedBlockingQueue<Runnable>(), new ThreadFactoryWithPriority(Thread.NORM_PRIORITY-1, /* daemon */ true));
}
@Override
public int getReasonableThreadPoolSize() {
return REASONABLE_THREAD_POOL_SIZE;
}
}