ejemplos - hilos spring java
Nombrar hilos y grupos de hilos de ExecutorService (13)
Digamos que tengo una aplicación que utiliza el marco Executor
como tal
Executors.newSingleThreadExecutor().submit(new Runnable(){
@Override
public void run(){
// do stuff
}
}
Cuando ejecuto esta aplicación en el depurador, se crea un subproceso con el siguiente nombre (predeterminado): Thread[pool-1-thread-1]
. Como puede ver, esto no es muy útil y, por lo que puedo decir, el marco Executor
no proporciona una manera fácil de nombrar los subprocesos creados o grupos de subprocesos.
Entonces, ¿cómo hace uno para proporcionar nombres para los subprocesos / subprocesos? Por ejemplo, Thread[FooPool-FooThread]
.
Esta es mi fábrica personalizada que proporciona nombres personalizados para analizadores de volcado de hilo. Por lo general, solo doy tf=null
para reutilizar la fábrica de hilos por defecto de JVM. Este sitio web tiene una fábrica de hilos más avanzada.
public class SimpleThreadFactory implements ThreadFactory {
private ThreadFactory tf;
private String nameSuffix;
public SimpleThreadFactory (ThreadFactory tf, String nameSuffix) {
this.tf = tf!=null ? tf : Executors.defaultThreadFactory();
this.nameSuffix = nameSuffix;
}
@Override public Thread newThread(Runnable task) {
// default "pool-1-thread-1" to "pool-1-thread-1-myapp-MagicTask"
Thread thread=tf.newThread(task);
thread.setName(thread.getName()+"-"+nameSuffix);
return thread;
}
}
- - - - -
ExecutorService es = Executors.newFixedThreadPool(4, new SimpleThreadFactory(null, "myapp-MagicTask") );
Para su comodidad, este es un bucle de volcado de subprocesos para depuración.
ThreadMXBean mxBean=ManagementFactory.getThreadMXBean();
long[] tids = mxBean.getAllThreadIds();
System.out.println("------------");
System.out.println("ThreadCount="+tids.length);
for(long tid : tids) {
ThreadInfo mxInfo=mxBean.getThreadInfo(tid);
if (mxInfo==null) {
System.out.printf("%d %s/n", tid, "Thread not found");
} else {
System.out.printf("%d %s, state=%s, suspended=%d, lockowner=%d %s/n"
, mxInfo.getThreadId(), mxInfo.getThreadName()
, mxInfo.getThreadState().toString()
, mxInfo.isSuspended()?1:0
, mxInfo.getLockOwnerId(), mxInfo.getLockOwnerName()
);
}
}
Extender ThreadFactory
public interface ThreadFactory
Un objeto que crea nuevos hilos a pedido. El uso de fábricas de subprocesos elimina el cableado de las llamadas al nuevo subproceso, permitiendo que las aplicaciones usen subclases de subprocesos especiales, prioridades, etc.
Thread newThread(Runnable r)
Construye un nuevo hilo. Las implementaciones también pueden inicializar prioridad, nombre, estado del daemon, ThreadGroup, etc.
Código de muestra:
import java.util.concurrent.*;
import java.util.concurrent.atomic.*;
import java.util.concurrent.ThreadPoolExecutor.DiscardPolicy;
class SimpleThreadFactory implements ThreadFactory {
String name;
AtomicInteger threadNo = new AtomicInteger(0);
public SimpleThreadFactory (String name){
this.name = name;
}
public Thread newThread(Runnable r) {
String threadName = name+":"+threadNo.incrementAndGet();
System.out.println("threadName:"+threadName);
return new Thread(r,threadName );
}
public static void main(String args[]){
SimpleThreadFactory factory = new SimpleThreadFactory("Factory Thread");
ThreadPoolExecutor executor= new ThreadPoolExecutor(1,1,60,
TimeUnit.SECONDS,new ArrayBlockingQueue<Runnable>(1),new ThreadPoolExecutor.DiscardPolicy());
final ExecutorService executorService = Executors.newFixedThreadPool(5,factory);
for ( int i=0; i < 100; i++){
executorService.submit(new Runnable(){
public void run(){
System.out.println("Thread Name in Runnable:"+Thread.currentThread().getName());
}
});
}
executorService.shutdown();
}
}
salida:
java SimpleThreadFactory
thread no:1
thread no:2
Thread Name in Runnable:Factory Thread:1
Thread Name in Runnable:Factory Thread:2
thread no:3
thread no:4
Thread Name in Runnable:Factory Thread:3
Thread Name in Runnable:Factory Thread:4
thread no:5
Thread Name in Runnable:Factory Thread:5
.... etc
Guava casi siempre tiene lo que need .
ThreadFactory namedThreadFactory =
new ThreadFactoryBuilder().setNameFormat("my-sad-thread-%d").build()
y pasarlo a su ExecutorService
.
Hay una RFE abierta para esto con Oracle. De los comentarios del empleado de Oracle parece que no entienden el problema y no lo arreglarán. Es una de estas cosas que es muy fácil de soportar en el JDK (sin romper la compatibilidad hacia atrás) por lo que es una pena que el RFE sea mal entendido.
Como se señaló, debe implementar su propia ThreadFactory . Si no desea utilizar Guava o Apache Commons solo para este fin, proporciono aquí una implementación de ThreadFactory
que puede usar. Es exactamente similar a lo que obtienes del JDK, excepto por la capacidad de establecer el prefijo del nombre del hilo en algo más que "grupo".
package org.demo.concurrency;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicInteger;
/**
* ThreadFactory with the ability to set the thread name prefix.
* This class is exactly similar to
* {@link java.util.concurrent.Executors#defaultThreadFactory()}
* from JDK8, except for the thread naming feature.
*
* <p>
* The factory creates threads that have names on the form
* <i>prefix-N-thread-M</i>, where <i>prefix</i>
* is a string provided in the constructor, <i>N</i> is the sequence number of
* this factory, and <i>M</i> is the sequence number of the thread created
* by this factory.
*/
public class ThreadFactoryWithNamePrefix implements ThreadFactory {
// Note: The source code for this class was based entirely on
// Executors.DefaultThreadFactory class from the JDK8 source.
// The only change made is the ability to configure the thread
// name prefix.
private static final AtomicInteger poolNumber = new AtomicInteger(1);
private final ThreadGroup group;
private final AtomicInteger threadNumber = new AtomicInteger(1);
private final String namePrefix;
/**
* Creates a new ThreadFactory where threads are created with a name prefix
* of <code>prefix</code>.
*
* @param prefix Thread name prefix. Never use a value of "pool" as in that
* case you might as well have used
* {@link java.util.concurrent.Executors#defaultThreadFactory()}.
*/
public ThreadFactoryWithNamePrefix(String prefix) {
SecurityManager s = System.getSecurityManager();
group = (s != null) ? s.getThreadGroup()
: Thread.currentThread().getThreadGroup();
namePrefix = prefix + "-"
+ poolNumber.getAndIncrement()
+ "-thread-";
}
@Override
public Thread newThread(Runnable r) {
Thread t = new Thread(group, r,
namePrefix + threadNumber.getAndIncrement(),
0);
if (t.isDaemon()) {
t.setDaemon(false);
}
if (t.getPriority() != Thread.NORM_PRIORITY) {
t.setPriority(Thread.NORM_PRIORITY);
}
return t;
}
}
Cuando quiera usarlo, simplemente aproveche el hecho de que todos los métodos de los Executors
permiten proporcionar su propio ThreadFactory
.
Esta
Executors.newSingleThreadExecutor();
dará un ExecutorService donde los hilos se denominan pool-N-thread-M
pero al usar
Executors.newSingleThreadExecutor(new ThreadFactoryWithNamePrefix("primecalc");
obtendrá un ExecutorService donde los hilos se denominan primecalc-N-thread-M
. Voila!
Puede escribir su propia implementación de ThreadFactory, utilizando, por ejemplo, alguna implementación existente (como por ejemplo, DefaultHhreadFactory) y cambiar el nombre al final.
Ejemplo de implementación de ThreadFactory:
class ThreadFactoryWithCustomName implements ThreadFactory {
private final ThreadFactory threadFactory;
private final String name;
public ThreadFactoryWithCustomName(final ThreadFactory threadFactory, final String name) {
this.threadFactory = threadFactory;
this.name = name;
}
@Override
public Thread newThread(final Runnable r) {
final Thread thread = threadFactory.newThread(r);
thread.setName(name);
return thread;
}
}
Y el uso:
Executors.newSingleThreadExecutor(new ThreadFactoryWithCustomName(
Executors.defaultThreadFactory(),
"customName")
);
Puede intentar proporcionar su propia fábrica de hilos, que creará un hilo con los nombres apropiados. Aquí hay un ejemplo:
class YourThreadFactory implements ThreadFactory {
public Thread newThread(Runnable r) {
return new Thread(r, "Your name");
}
}
Executors.newSingleThreadExecutor(new YourThreadFactory()).submit(someRunnable);
Puede suministrar un ThreadFactory
a newSingleThreadScheduledExecutor(ThreadFactory threadFactory)
. La fábrica será responsable de crear hilos y podrá nombrarlos.
Para citar el Javadoc :
Creando nuevos hilos
Los nuevos hilos se crean usando
ThreadFactory
. Si no se especifica lo contrario, se utilizaExecutors.defaultThreadFactory()
, que crea subprocesos para que todos estén en el mismoThreadGroup
y con la misma prioridadNORM_PRIORITY
y el estado no-demonio. Al suministrar unThreadFactory
diferente, puede alterar el nombre del hilo, el grupo de hilos, la prioridad, el estado del daemon, etc. Si unThreadFactory
no puede crear un hilo cuando se le pregunta al devolver null desdenewThread
, el ejecutor continuará, pero es posible que no pueda ejecutar cualquier tarea
Si está utilizando Spring, hay CustomizableThreadFactory
para el cual puede establecer un prefijo de nombre de hilo.
Ejemplo:
ExecutorService alphaExecutor =
Executors.newFixedThreadPool(10, new CustomizableThreadFactory("alpha-"));
También puede cambiar el nombre de su hilo después, mientras se ejecuta el hilo:
Thread.currentThread().setName("FooName");
Eso podría ser de interés si, por ejemplo, está utilizando el mismo ThreadFactory para diferentes tipos de tareas.
Una manera rápida y sucia es usar Thread.currentThread().setName(myName);
en el método run()
BasicThreadFactory
de Apache commons-lang también es útil para proporcionar el comportamiento de nomenclatura. En lugar de escribir una clase interna anónima, puede usar el Creador para nombrar los hilos como desee. Aquí está el ejemplo de los javadocs:
// Create a factory that produces daemon threads with a naming pattern and
// a priority
BasicThreadFactory factory = new BasicThreadFactory.Builder()
.namingPattern("workerthread-%d")
.daemon(true)
.priority(Thread.MAX_PRIORITY)
.build();
// Create an executor service for single-threaded execution
ExecutorService exec = Executors.newSingleThreadExecutor(factory);
Executors.newSingleThreadExecutor(r -> new Thread(r, "someName")).submit(getJob());
Runnable getJob() {
return () -> {
// your job
};
}
private class TaskThreadFactory implements ThreadFactory
{
@Override
public Thread newThread(Runnable r) {
Thread t = new Thread(r, "TASK_EXECUTION_THREAD");
return t;
}
}
Pase ThreadFactory a un ejecutorservice y está listo para ir