La interfaz ThreadFactory definida en el paquete java.util.concurrent se basa en el patrón de diseño de fábrica . Como su nombre indica, se utiliza para crear nuevos hilos bajo demanda. Los hilos se pueden crear de dos maneras:
1. Crear una clase que extienda la clase Thread y luego crear sus objetos.
Java
import java.io.*; class GFG { public static void main(String[] args) { // Creating a thread Thread thread = new CustomThread(); thread.start(); // Starting execution of the created // thread } } // Creating a class that extends the Thread class class CustomThread extends Thread { @Override public void run() { System.out.println("This is a thread"); } }
This is a thread
2. Crear una clase que implemente la interfaz Runnable y luego usar su objeto para crear subprocesos.
Java
/*package whatever //do not write package name here */ import java.io.*; class GFG { public static void main(String[] args) { // Creating a Runnable object Runnable task = new Task(); // Creating a thread using the Runnable object Thread thread = new Thread(task); // Starting the execution of the created thread thread.start(); } } class Task implements Runnable { @Override public void run() { System.out.println("This is a thread"); } }
This is a thread
Sin embargo, ThreadFactory es otra opción para crear nuevos hilos. Esta interfaz proporciona un método de fábrica que crea y devuelve nuevos subprocesos cuando se le llama. Este método de fábrica toma un objeto Runnable como argumento y crea un nuevo hilo usándolo.
La jerarquía de ThreadFactory
java.util.concurrent ↳ Interface ThreadFactory
Implementación de la interfaz ThreadFactory
Dado que ThreadFactory es una interfaz, el método de fábrica definido en su interior debe implementarse primero para poder usarse. Aquí está la implementación más simple de la interfaz ThreadFactory:
Java
import java.util.concurrent.ThreadFactory; import java.io.*; class CustomThreadFactory implements ThreadFactory { // newThread is a factory method // provided by ThreadFactory public Thread newThread(Runnable command) { return new Thread(command); } }
Ahora, podemos crear objetos de la clase CustomThreadFactory y usar su método newThread(Runnable command) para crear nuevos subprocesos a pedido. En la implementación anterior, el método newThread simplemente crea un nuevo hilo llamando al constructor Thread que toma un comando Runnable como parámetro.
Hay muchas clases (como ScheduledThreadPoolExecutor , ThreadPoolExecutor , etc.) que usan fábricas de subprocesos para crear nuevos subprocesos cuando es necesario. Esas clases tienen constructores que aceptan ThreadFactory como argumento. Si no se proporciona ningún ThreadFactory personalizado, entonces usan la implementación predeterminada de la interfaz ThreadFactory.
La clase Executors en el paquete java.util.concurrent proporciona el método estático Executors.defaultThreadFactory() que devuelve una implementación predeterminada de la interfaz ThreadFactory.
Ejemplo: El siguiente código de ejemplo muestra la interfaz ThreadFactory.
Java
// Java code to demonstrate ThreadFactory interface import java.util.concurrent.ThreadFactory; import java.io.*; import java.util.ArrayList; class ThreadFactoryExample { public static void main(String[] args) { // Creating a CustomThreadFactory object CustomThreadFactory threadFactory = new CustomThreadFactory(); // Creating Runnable objects using the lambda // expression Runnable command1 = () -> System.out.println("Command 1 executed"); Runnable command2 = () -> System.out.println("Command 2 executed"); Runnable command3 = () -> System.out.println("Command 3 executed"); Runnable command4 = () -> System.out.println("Command 4 executed"); Runnable command5 = () -> System.out.println("Command 5 executed"); // Putting the commands in an ArrayList ArrayList<Runnable> array = new ArrayList<>(5); array.add(command1); array.add(command2); array.add(command3); array.add(command4); array.add(command5); // creating threads and running them for (Runnable command : array) { threadFactory.newThread(command).start(); } // print the thread count System.out.println( "Total number of threads created using CustomThreadFactory = " + threadFactory.getCount()); } } // ThreadFactory class class CustomThreadFactory implements ThreadFactory { // stores the thread count private int count = 0; // returns the thread count public int getCount() { return count; } // Factory method @Override public Thread newThread(Runnable command) { count++; return new Thread(command); } }
Command 1 executed Command 2 executed Command 4 executed Command 3 executed Command 5 executed Total number of threads created using CustomThreadFactory = 5
¿Por qué usar ThreadFactory?
En el ejemplo anterior, el método de fábrica newThread(Runnable) finalmente crea un nuevo hilo usando el comando Runnable dado. Entonces, ¿por qué usar ThreadFactory? Podríamos crear subprocesos directamente a partir de los comandos Runnable llamando al constructor Thread que hicimos en el método newThread(Runnable). Aquí hay algunas razones,
- Podemos dar a los subprocesos nombres personalizados más significativos. Ayuda a analizar sus propósitos y cómo funcionan.
- Podemos tener estadísticas sobre los hilos creados, como el recuento de hilos y otros detalles. Podemos restringir la creación de nuevos hilos en función de las estadísticas.
- Podemos establecer el estado del demonio de los hilos.
- Podemos establecer la prioridad del hilo.
- Podemos tener todas las características confinadas en una clase.
Fábrica de subprocesos predeterminada
Es la fábrica de subprocesos predeterminada implementada por el método estático Executors.defaultThreadFactory() . Muchas clases utilizan esta ThreadFactory predeterminada (como ScheduledThreadPoolExecutor , ThreadPoolExecutor , etc.) cuando no reciben ninguna ThreadFactory personalizada. Esas clases crean nuevos subprocesos utilizando ThreadFactory predeterminado. Este ThreadFactory predeterminado crea todos los subprocesos nuevos en el mismo ThreadGroup (un ThreadGroup representa un grupo de subprocesos). Todos los subprocesos nuevos creados no son daemon con la prioridad establecida en el valor más pequeño de Thread.NORM_PRIORITY y la prioridad máxima permitida en ThreadGroup. Los subprocesos creados por este ThreadFactory predeterminado reciben nombres en forma de pool-N-thread-M(Como ejemplos, grupo-1-hilo-1, grupo-1-hilo-2, grupo-2-hilo-1, etc.) donde N es el número de secuencia de esta fábrica y M es el número de secuencia de los hilos creados por esta fábrica.
Ejemplo: El siguiente ejemplo demuestra cómo se puede usar ThreadFactory predeterminado.
Java
// Java program to demonstrate default // ThreadFactory import java.io.*; import java.util.concurrent.Executors; import java.util.concurrent.ThreadFactory; class DefaultThreadFactoryExample { public static void main(String[] args) { // Default ThreadFactory ThreadFactory threadFactory = Executors.defaultThreadFactory(); for (int i = 1; i < 10; i++) { // Creating new threads with the default // ThreadFactory Thread thread = threadFactory.newThread(new Command()); // print the thread names System.out.println( "Name given by threadFactory = " + thread.getName()); // run the thread thread.start(); } } } class Command implements Runnable { @Override public void run() { // Run some code } }
Name given by threadFactory = pool-1-thread-1 Name given by threadFactory = pool-1-thread-2 Name given by threadFactory = pool-1-thread-3 Name given by threadFactory = pool-1-thread-4 Name given by threadFactory = pool-1-thread-5 Name given by threadFactory = pool-1-thread-6 Name given by threadFactory = pool-1-thread-7 Name given by threadFactory = pool-1-thread-8 Name given by threadFactory = pool-1-thread-9
Tenga en cuenta los nombres de los subprocesos dados por defecto ThreadFactory. Ha creado 9 subprocesos y todos los subprocesos están en el mismo ThreadGroup. Todos los subprocesos se crean utilizando la misma fábrica de subprocesos (por lo que los nombres de los subprocesos tienen la forma de pool -1 -thread-M ).
Ejemplo:
Java
// Java program to demonstrate ThreadFactory // using default implementation import java.io.*; import java.util.concurrent.Executors; import java.util.concurrent.ThreadFactory; class DefaultThreadFactoryExample { public static void main(String[] args) { for (int i = 1; i < 10; i++) { // Default ThreadFactory ThreadFactory threadFactory = Executors.defaultThreadFactory(); // Creating new threads with the default // ThreadFactory Thread thread = threadFactory.newThread(new Command()); // print the thread name System.out.println( "Name given by threadFactory = " + thread.getName()); // start the thread thread.start(); } } } class Command implements Runnable { @Override public void run() { // Run some code } }
Name given by threadFactory = pool-1-thread-1 Name given by threadFactory = pool-2-thread-1 Name given by threadFactory = pool-3-thread-1 Name given by threadFactory = pool-4-thread-1 Name given by threadFactory = pool-5-thread-1 Name given by threadFactory = pool-6-thread-1 Name given by threadFactory = pool-7-thread-1 Name given by threadFactory = pool-8-thread-1 Name given by threadFactory = pool-9-thread-1
Aquí, hemos utilizado 9 ThreadFactories predeterminados diferentes (¡en cada bucle estamos creando uno nuevo!). Entonces, cada subproceso está en un grupo de subprocesos diferente y, por lo tanto, los subprocesos reciben un nombre en forma de pool-N-thread-1 .
La implementación predeterminada de ThreadFactory crea subprocesos que no son demonios con prioridad normal y da nombres en forma de pool-N-thread-M que no contiene información sobre cómo funcionan y qué hacen. Esto crea muchos problemas en la depuración y otros propósitos importantes. Sin embargo, este problema se puede resolver utilizando un ThreadFactory personalizado que puede dar nombres más significativos a los subprocesos y puede establecer el daemon y los estados de prioridad.
Métodos de ThreadFactory
MÉTODO |
DESCRIPCIÓN |
---|---|
newThread(ejecutable r) | Construye un hilo nuevo. |