"implementa Runnable" frente a "extiende Thread" en Java
Desde el tiempo que pasé con los hilos en Java
, encontré estas dos formas de escribir hilos:
Con implementos Runnable
:
public class MyRunnable implements Runnable {
public void run() {
//Code
}
}
//Started with a "new Thread(new MyRunnable()).start()" call
O, con extiende Thread
:
public class MyThread extends Thread {
public MyThread() {
super("MyThread");
}
public void run() {
//Code
}
}
//Started with a "new MyThread().start()" call
¿Existe alguna diferencia significativa entre estos dos bloques de código?
Sí: los implementos Runnable
son la forma preferida de hacerlo, en mi opinión. Realmente no estás especializando el comportamiento del hilo. Simplemente le estás dando algo para que funcione. Eso significa que la composición es el camino filosóficamente "más puro" a seguir.
En términos prácticos , significa que también puedes implementar Runnable
y extender desde otra clase... y también puedes implementar Runnable
mediante una expresión lambda a partir de Java 8.
tl;dr: implementa Runnable es mejor. Sin embargo, la advertencia es importante .
En general, recomendaría usar algo como Runnable
en lugar de Thread
porque le permite mantener su trabajo vinculado de manera flexible con su elección de concurrencia. Por ejemplo, si usa a Runnable
y decide más adelante que esto en realidad no requiere su propio Thread
, puede simplemente llamar a threadA.run().
Advertencia: por aquí, desaconsejo encarecidamente el uso de subprocesos sin formato. Prefiero el uso de Callables y FutureTasks (del javadoc: "Un cálculo asincrónico cancelable"). La integración de tiempos de espera, la cancelación adecuada y la agrupación de subprocesos del soporte de concurrencia moderno son mucho más útiles para mí que montones de subprocesos sin procesar.
Seguimiento: existe un FutureTask
constructor que le permite usar Runnables (si es con lo que se siente más cómodo) y aún así obtener el beneficio de las herramientas de concurrencia modernas. Para citar el javadoc :
Si no necesita un resultado en particular, considere usar construcciones de la forma:
Future<?> f = new FutureTask<Object>(runnable, null)
Entonces, si reemplazamos su runnable
con su threadA
, obtenemos lo siguiente:
new FutureTask<Object>(threadA, null)
Otra opción que te permite estar más cerca de Runnables es ThreadPoolExecutor . Puede utilizar el método de ejecución para pasar un Runnable para ejecutar "la tarea dada en algún momento en el futuro".
Si desea intentar usar un grupo de subprocesos, el fragmento de código anterior sería algo como lo siguiente (usando el método de fábrica Executors.newCachedThreadPool() ):
ExecutorService es = Executors.newCachedThreadPool();
es.execute(new ThreadA());
Moraleja de la historia:
Herede solo si desea anular algún comportamiento.
O mejor dicho debería leerse como:
Hereda menos, interactúa más.
Si desea implementar o ampliar cualquier otra clase, entonces Runnable
lo más preferible es la interfaz; de lo contrario, si no desea que ninguna otra clase se extienda o implemente, entonces Thread
es preferible la clase.
La diferencia más común es
Cuando tomas extends Thread
clases, después de eso no puedes extender ninguna otra clase que necesites. (Como sabes, Java no permite heredar más de una clase).
Cuando lo hace implements Runnable
, puede ahorrar espacio para que su clase pueda ampliar cualquier otra clase en el futuro o ahora.
Java no admite herencias múltiples, lo que significa que solo puede extender una clase en Java, por lo que una vez que extendió la clase Thread perdió la oportunidad y no puede extender ni heredar otra clase en Java.
En la programación orientada a objetos, ampliar una clase generalmente significa agregar nuevas funcionalidades y modificar o mejorar comportamientos. Si no realizamos ninguna modificación en Thread, utilice la interfaz Runnable.
La interfaz ejecutable representa una tarea que puede ejecutarse mediante subprocesos simples o ejecutores o cualquier otro medio. por lo que la separación lógica de Tarea como Ejecutable que de Hilo es una buena decisión de diseño.
Separar la tarea como Runnable significa que podemos reutilizar la tarea y también tenemos la libertad de ejecutarla desde diferentes medios. ya que no puedes reiniciar un hilo una vez que se completa. nuevamente Runnable vs Thread para la tarea, Runnable es el ganador.
El diseñador de Java reconoce esto y es por eso que los ejecutores aceptan Runnable como tarea y tienen un subproceso de trabajo que ejecuta esas tareas.
Heredar todos los métodos de Thread supone una sobrecarga adicional solo para representar una Tarea que se puede realizar fácilmente con Runnable.
Cortesía de javarevisited.blogspot.com
Estas fueron algunas de las diferencias notables entre Thread y Runnable en Java. Si conoce alguna otra diferencia entre Thread y Runnable, compártala a través de los comentarios. Yo personalmente uso Runnable over Thread para este escenario y recomiendo usar la interfaz Runnable o Callable según sus requisitos.
Sin embargo, la diferencia significativa es.
Cuando extends Thread
clasificas, cada uno de tus hilos crea un objeto único y se asocia con él. Cuando lo haces implements Runnable
, comparte el mismo objeto con varios subprocesos.
Una cosa que me sorprende que aún no se haya mencionado es que la implementación Runnable
hace que la clase sea más flexible.
Si extiendes el hilo, la acción que estás realizando siempre estará en un hilo. Sin embargo, si lo implementa, Runnable
no tiene por qué ser así. Puede ejecutarlo en un subproceso, o pasarlo a algún tipo de servicio ejecutor, o simplemente pasarlo como una tarea dentro de una aplicación de un solo subproceso (tal vez para ejecutarlo más adelante, pero dentro del mismo subproceso). Las opciones son mucho más abiertas si solo las usas Runnable
que si te vinculas a Thread
.
En realidad, no es prudente compararse Runnable
entre Thread
sí.
Estos dos tienen una dependencia y relación en subprocesos múltiples al igual que Wheel and Engine
la relación de un vehículo de motor.
Yo diría que solo hay una forma de realizar subprocesos múltiples con dos pasos. Déjame aclarar mi punto.
Ejecutable:
al implementarlo interface Runnable
significa que estás creando algo que está run able
en un hilo diferente. Ahora bien, crear algo que pueda ejecutarse dentro de un subproceso (ejecutable dentro de un subproceso) no significa crear un subproceso.
Entonces la clase MyRunnable
no es más que una clase ordinaria con un void run
método. Y sus objetos serán algunos objetos ordinarios con solo un método run
que se ejecutará normalmente cuando se llame. (a menos que pasemos el objeto en un hilo).
Subproceso:
class Thread
Yo diría que es una clase muy especial con la capacidad de iniciar un nuevo subproceso que en realidad permite subprocesos múltiples a través de su start()
método.
¿Por qué no es prudente comparar?
Porque los necesitamos a ambos para subprocesos múltiples.
Para subprocesos múltiples necesitamos dos cosas:
- Algo que puede ejecutarse dentro de un Thread (Runnable).
- Algo Que pueda iniciar un nuevo Hilo (Thread).
Entonces, técnica y teóricamente ambos son necesarios para iniciar un hilo, uno lo ejecutará y el otro lo hará funcionar (como Wheel and Engine
en un vehículo de motor).
Es por eso que no puedes iniciar un hilo; MyRunnable
debes pasarlo a una instancia de Thread
.
Pero es posible crear y ejecutar un hilo solo class Thread
porque Class Thread
se implementa, Runnable
por lo que todos sabemos que Thread
también es Runnable
interno.
Finalmente Thread
, son Runnable
complementos entre sí para subprocesos múltiples, no competidores ni reemplazos.