com.signalfx.shaded.jetty.util.thread.ExecutorThreadPool Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of signalfx-codahale Show documentation
Show all versions of signalfx-codahale Show documentation
Dropwizard Codahale metrics plugin for signalfx
The newest version!
//
// ========================================================================
// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package com.signalfx.shaded.jetty.util.thread;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import com.signalfx.shaded.jetty.util.ProcessorUtils;
import com.signalfx.shaded.jetty.util.annotation.ManagedAttribute;
import com.signalfx.shaded.jetty.util.annotation.ManagedObject;
import com.signalfx.shaded.jetty.util.component.ContainerLifeCycle;
import com.signalfx.shaded.jetty.util.component.Dumpable;
import com.signalfx.shaded.jetty.util.component.DumpableCollection;
/**
* A {@link com.signalfx.shaded.jetty.util.thread.ThreadPool.SizedThreadPool} wrapper around {@link ThreadPoolExecutor}.
*/
@ManagedObject("A thread pool")
public class ExecutorThreadPool extends ContainerLifeCycle implements ThreadPool.SizedThreadPool, TryExecutor
{
private final ThreadPoolExecutor _executor;
private final ThreadPoolBudget _budget;
private final ThreadGroup _group;
private String _name = "etp" + hashCode();
private int _minThreads;
private int _reservedThreads = -1;
private TryExecutor _tryExecutor = TryExecutor.NO_TRY;
private int _priority = Thread.NORM_PRIORITY;
private boolean _daemon;
private boolean _detailedDump;
public ExecutorThreadPool()
{
this(200, 8);
}
public ExecutorThreadPool(int maxThreads)
{
this(maxThreads, Math.min(8, maxThreads));
}
public ExecutorThreadPool(int maxThreads, int minThreads)
{
this(maxThreads, minThreads, new LinkedBlockingQueue<>());
}
public ExecutorThreadPool(int maxThreads, int minThreads, BlockingQueue queue)
{
this(new ThreadPoolExecutor(maxThreads, maxThreads, 60, TimeUnit.SECONDS, queue), minThreads, -1, null);
}
public ExecutorThreadPool(ThreadPoolExecutor executor)
{
this(executor, -1);
}
public ExecutorThreadPool(ThreadPoolExecutor executor, int reservedThreads)
{
this(executor, reservedThreads, null);
}
public ExecutorThreadPool(ThreadPoolExecutor executor, int reservedThreads, ThreadGroup group)
{
this(executor, Math.min(ProcessorUtils.availableProcessors(), executor.getCorePoolSize()), reservedThreads, group);
}
private ExecutorThreadPool(ThreadPoolExecutor executor, int minThreads, int reservedThreads, ThreadGroup group)
{
int maxThreads = executor.getMaximumPoolSize();
if (maxThreads < minThreads)
{
executor.shutdownNow();
throw new IllegalArgumentException("max threads (" + maxThreads + ") cannot be less than min threads (" + minThreads + ")");
}
_executor = executor;
_executor.setThreadFactory(this::newThread);
_group = group;
_minThreads = minThreads;
_reservedThreads = reservedThreads;
_budget = new ThreadPoolBudget(this);
}
/**
* @return the name of the this thread pool
*/
@ManagedAttribute("name of this thread pool")
public String getName()
{
return _name;
}
/**
* @param name the name of this thread pool, used to name threads
*/
public void setName(String name)
{
if (isRunning())
throw new IllegalStateException(getState());
_name = name;
}
@Override
@ManagedAttribute("minimum number of threads in the pool")
public int getMinThreads()
{
return _minThreads;
}
@Override
public void setMinThreads(int threads)
{
_minThreads = threads;
}
@Override
@ManagedAttribute("maximum number of threads in the pool")
public int getMaxThreads()
{
return _executor.getMaximumPoolSize();
}
@Override
public void setMaxThreads(int threads)
{
if (_budget != null)
_budget.check(threads);
_executor.setCorePoolSize(threads);
_executor.setMaximumPoolSize(threads);
}
/**
* @return the maximum thread idle time in ms.
* @see #setIdleTimeout(int)
*/
@ManagedAttribute("maximum time a thread may be idle in ms")
public int getIdleTimeout()
{
return (int)_executor.getKeepAliveTime(TimeUnit.MILLISECONDS);
}
/**
* Sets the maximum thread idle time in ms.
* Threads that are idle for longer than this
* period may be stopped.
*
* @param idleTimeout the maximum thread idle time in ms.
* @see #getIdleTimeout()
*/
public void setIdleTimeout(int idleTimeout)
{
_executor.setKeepAliveTime(idleTimeout, TimeUnit.MILLISECONDS);
}
/**
* @return number of reserved threads or -1 to indicate that the number is heuristically determined
* @see #setReservedThreads(int)
*/
@ManagedAttribute("the number of reserved threads in the pool")
public int getReservedThreads()
{
if (isStarted())
return getBean(ReservedThreadExecutor.class).getCapacity();
return _reservedThreads;
}
/**
* Sets the number of reserved threads.
*
* @param reservedThreads number of reserved threads or -1 to determine the number heuristically
* @see #getReservedThreads()
*/
public void setReservedThreads(int reservedThreads)
{
if (isRunning())
throw new IllegalStateException(getState());
_reservedThreads = reservedThreads;
}
public void setThreadsPriority(int priority)
{
_priority = priority;
}
public int getThreadsPriority()
{
return _priority;
}
/**
* @return whether this thread pool uses daemon threads
* @see #setDaemon(boolean)
*/
@ManagedAttribute("whether this thread pool uses daemon threads")
public boolean isDaemon()
{
return _daemon;
}
/**
* @param daemon whether this thread pool uses daemon threads
* @see Thread#setDaemon(boolean)
*/
public void setDaemon(boolean daemon)
{
_daemon = daemon;
}
@ManagedAttribute("reports additional details in the dump")
public boolean isDetailedDump()
{
return _detailedDump;
}
public void setDetailedDump(boolean detailedDump)
{
_detailedDump = detailedDump;
}
@Override
@ManagedAttribute("number of threads in the pool")
public int getThreads()
{
return _executor.getPoolSize();
}
@Override
@ManagedAttribute("number of idle threads in the pool")
public int getIdleThreads()
{
return _executor.getPoolSize() - _executor.getActiveCount();
}
@Override
public void execute(Runnable command)
{
_executor.execute(command);
}
@Override
public boolean tryExecute(Runnable task)
{
TryExecutor tryExecutor = _tryExecutor;
return tryExecutor != null && tryExecutor.tryExecute(task);
}
@Override
@ManagedAttribute(value = "thread pool is low on threads", readonly = true)
public boolean isLowOnThreads()
{
return getThreads() == getMaxThreads() && _executor.getQueue().size() >= getIdleThreads();
}
@Override
protected void doStart() throws Exception
{
if (_executor.isShutdown())
throw new IllegalStateException("This thread pool is not restartable");
for (int i = 0; i < _minThreads; ++i)
{
_executor.prestartCoreThread();
}
_tryExecutor = new ReservedThreadExecutor(this, _reservedThreads);
addBean(_tryExecutor);
super.doStart();
}
@Override
protected void doStop() throws Exception
{
super.doStop();
removeBean(_tryExecutor);
_tryExecutor = TryExecutor.NO_TRY;
_executor.shutdownNow();
_budget.reset();
}
@Override
public void join() throws InterruptedException
{
_executor.awaitTermination(Long.MAX_VALUE, TimeUnit.MILLISECONDS);
}
@Override
public ThreadPoolBudget getThreadPoolBudget()
{
return _budget;
}
protected Thread newThread(Runnable job)
{
Thread thread = new Thread(_group, job);
thread.setDaemon(isDaemon());
thread.setPriority(getThreadsPriority());
thread.setName(getName() + "-" + thread.getId());
return thread;
}
@Override
public void dump(Appendable out, String indent) throws IOException
{
String prefix = getName() + "-";
List threads = Thread.getAllStackTraces().entrySet().stream()
.filter(entry -> entry.getKey().getName().startsWith(prefix))
.map(entry ->
{
Thread thread = entry.getKey();
StackTraceElement[] frames = entry.getValue();
String knownMethod = null;
for (StackTraceElement frame : frames)
{
if ("getTask".equals(frame.getMethodName()) && frame.getClassName().endsWith("ThreadPoolExecutor"))
{
knownMethod = "IDLE ";
break;
}
else if ("reservedWait".equals(frame.getMethodName()) && frame.getClassName().endsWith("ReservedThread"))
{
knownMethod = "RESERVED ";
break;
}
else if ("select".equals(frame.getMethodName()) && frame.getClassName().endsWith("SelectorProducer"))
{
knownMethod = "SELECTING ";
break;
}
else if ("accept".equals(frame.getMethodName()) && frame.getClassName().contains("ServerConnector"))
{
knownMethod = "ACCEPTING ";
break;
}
}
String known = knownMethod == null ? "" : knownMethod;
return new Dumpable()
{
@Override
public void dump(Appendable out, String indent) throws IOException
{
StringBuilder b = new StringBuilder();
b.append(thread.getId())
.append(" ")
.append(thread.getName())
.append(" p=").append(thread.getPriority())
.append(" ")
.append(known)
.append(thread.getState().toString());
if (isDetailedDump())
{
if (known.isEmpty())
Dumpable.dumpObjects(out, indent, b.toString(), (Object[])frames);
else
Dumpable.dumpObject(out, b.toString());
}
else
{
b.append(" @ ").append(frames.length > 0 ? String.valueOf(frames[0]) : "");
Dumpable.dumpObject(out, b.toString());
}
}
@Override
public String dump()
{
return null;
}
};
})
.collect(Collectors.toList());
List jobs = Collections.emptyList();
if (isDetailedDump())
jobs = new ArrayList<>(_executor.getQueue());
dumpObjects(out, indent, threads, new DumpableCollection("jobs", jobs));
}
@Override
public String toString()
{
return String.format("%s[%s]@%x{%s,%d<=%d<=%d,i=%d,q=%d,%s}",
getClass().getSimpleName(),
getName(),
hashCode(),
getState(),
getMinThreads(),
getThreads(),
getMaxThreads(),
getIdleThreads(),
_executor.getQueue().size(),
_tryExecutor);
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy