new - "Implementa Runnable" vs "extiende Thread" en Java
runnable java thread (30)
- Java no admite la herencia múltiple, lo que significa que solo puede extender una clase en Java, así que una vez que extendió la clase
Thread
perdió su oportunidad y no puede extender o heredar otra clase en Java. - En la programación orientada a objetos, extender una clase generalmente significa agregar nueva funcionalidad, modificar o mejorar los comportamientos. Si no estamos haciendo ninguna modificación en
Thread
use la interfazRunnable
. -
Runnable
interfazRunnable
representa unaTask
que puede ser ejecutada por un soloThread
o porExecutors
o por cualquier otro medio. La separación lógica deTask
comoRunnable
queThread
es una buena decisión de diseño. - Separar tarea como
Runnable
significa que podemos reutilizar la tarea y también tenemos libertad para ejecutarla desde diferentes medios. Ya que no puede reiniciar unThread
una vez que se complete, nuevamenteRunnable
vsThread
para la tarea,Runnable
es el ganador. - El diseñador Java reconoce esto y es por eso que los
Executors
aceptanRunnable
comoTask
y tienen un hilo de trabajo que ejecuta esas tareas. - La herencia de todos los métodos
Thread
Runnable
una sobrecarga adicional solo para representar unaTask
que se puede hacer fácilmente conRunnable
.
Del tiempo que he pasado con hilos en Java, he encontrado estas dos formas de escribir hilos:
Con implements Runnable
:
public class MyRunnable implements Runnable {
public void run() {
//Code
}
}
//Started with a "new Thread(new MyRunnable()).start()" call
O, con extends Thread
:
public class MyThread extends Thread {
public MyThread() {
super("MyThread");
}
public void run() {
//Code
}
}
//Started with a "new MyThread().start()" call
¿Hay alguna diferencia significativa en estos dos bloques de código?
Bueno, tantas buenas respuestas, quiero agregar más sobre esto. Esto ayudará a comprender Extending v/s Implementing Thread
.
Extiende los enlaces de dos archivos de clase muy estrechamente y puede causar algunos bastante difíciles de tratar con el código.
Ambos enfoques hacen el mismo trabajo pero ha habido algunas diferencias.
La diferencia más común es
- Cuando extiende la clase Thread, después de eso no puede extender ninguna otra clase que requiera. (Como saben, Java no permite heredar más de una clase).
- Cuando implementa Runnable, puede guardar un espacio para su clase para extender cualquier otra clase en el futuro o ahora.
Sin embargo, una diferencia significativa entre la implementación de Runnable y la extensión de Thread es que
by extending Thread, each of your threads has a unique object associated with it, whereas implementing Runnable, many threads can share the same object instance.
El siguiente ejemplo te ayuda a entender más claramente
//Implement Runnable Interface...
class ImplementsRunnable implements Runnable {
private int counter = 0;
public void run() {
counter++;
System.out.println("ImplementsRunnable : Counter : " + counter);
}
}
//Extend Thread class...
class ExtendsThread extends Thread {
private int counter = 0;
public void run() {
counter++;
System.out.println("ExtendsThread : Counter : " + counter);
}
}
//Use above classes here in main to understand the differences more clearly...
public class ThreadVsRunnable {
public static void main(String args[]) throws Exception {
// Multiple threads share the same object.
ImplementsRunnable rc = new ImplementsRunnable();
Thread t1 = new Thread(rc);
t1.start();
Thread.sleep(1000); // Waiting for 1 second before starting next thread
Thread t2 = new Thread(rc);
t2.start();
Thread.sleep(1000); // Waiting for 1 second before starting next thread
Thread t3 = new Thread(rc);
t3.start();
// Creating new instance for every thread access.
ExtendsThread tc1 = new ExtendsThread();
tc1.start();
Thread.sleep(1000); // Waiting for 1 second before starting next thread
ExtendsThread tc2 = new ExtendsThread();
tc2.start();
Thread.sleep(1000); // Waiting for 1 second before starting next thread
ExtendsThread tc3 = new ExtendsThread();
tc3.start();
}
}
Salida del programa anterior.
ImplementsRunnable : Counter : 1
ImplementsRunnable : Counter : 2
ImplementsRunnable : Counter : 3
ExtendsThread : Counter : 1
ExtendsThread : Counter : 1
ExtendsThread : Counter : 1
En el enfoque de la interfaz Runnable, solo se crea una instancia de una clase y ha sido compartida por diferentes hilos. Por lo tanto, el valor del contador se incrementa para cada acceso a todos los subprocesos.
Considerando que, enfoque de clase de subproceso, debe tener que crear una instancia separada para cada acceso de subproceso. Por lo tanto, se asigna una memoria diferente para cada instancia de clase y cada una tiene un contador separado, el valor permanece igual, lo que significa que no se producirá ningún incremento porque ninguna de las referencias del objeto es igual.
¿Cuándo usar Runnable?
Utilice la interfaz Runnable cuando desee acceder al mismo recurso desde el grupo de subprocesos. Evite usar la clase Thread aquí, porque la creación de múltiples objetos consume más memoria y se convierte en una gran sobrecarga de rendimiento.
Una clase que implementa Runnable no es un hilo y solo una clase. Para que un Runnable se convierta en un Thread, debe crear una instancia de Thread y pasar a sí mismo como el destino.
En la mayoría de los casos, la interfaz de Runnable se debe usar si solo planea anular el método run()
y no otros métodos Thread. Esto es importante porque las clases no deben clasificarse a menos que el programador intente modificar o mejorar el comportamiento fundamental de la clase.
Cuando existe la necesidad de extender una superclase, implementar la interfaz Runnable es más apropiado que usar la clase Thread. Porque podemos extender otra clase mientras implementamos la interfaz Runnable para hacer un hilo.
¡Espero que esto sea de ayuda!
Con el lanzamiento de Java 8, ahora hay una tercera opción.
Runnable
es una interfaz funcional , lo que significa que se pueden crear instancias con expresiones lambda o referencias de métodos.
Tu ejemplo puede ser reemplazado por:
new Thread(() -> { /* Code here */ }).start()
o si desea usar un ExecutorService
y una referencia de método:
executor.execute(runner::run)
Estos no solo son mucho más cortos que sus ejemplos, sino que también tienen muchas de las ventajas que se indican en otras respuestas de usar Runnable
sobre Thread
, como la responsabilidad única y el uso de la composición porque no está especializando el comportamiento del hilo. De esta manera también evita crear una clase extra si todo lo que necesita es un Runnable
como lo hace en sus ejemplos.
Debe implementar Runnable, pero si está ejecutando en Java 5 o superior, no debe iniciarlo con un new Thread
sino usar un ExecutorService . Para más detalles, consulte: Cómo implementar subprocesos simples en Java .
Ejecutable porque:
- Deja más flexibilidad para que la implementación Runnable extienda otra clase.
- Separa el código de la ejecución.
- Le permite ejecutar su runnable desde un Thread Pool, el thread de eventos, o de cualquier otra forma en el futuro.
Incluso si no necesita nada de esto ahora, puede hacerlo en el futuro. Dado que no hay ningún beneficio en anular Thread, Runnable es una mejor solución.
En realidad, no es prudente comparar Runnable
y Thread
entre sí.
Estos dos tienen una dependencia y una relación en los subprocesos múltiples al igual que la relación entre la Wheel and Engine
del vehículo motorizado.
Diría que solo hay una forma de subprocesos múltiples con dos pasos. Déjame hacer mi punto.
Ejecutable:
Al implementar la interface Runnable
, significa que está creando algo que se run able
en un subproceso diferente. Ahora, crear algo que pueda ejecutarse dentro de un hilo (ejecutable dentro de un hilo) no significa crear un hilo.
Así que la clase MyRunnable
no es más que una clase ordinaria con un método de void run
. Y sus objetos serán algunos objetos ordinarios con solo un método que se ejecutará normalmente cuando se llame. (A menos que pasemos el objeto en un hilo).
Hilo:
class Thread
, diría Una clase muy especial con la capacidad de iniciar un nuevo Thread que realmente habilita el subprocesamiento múltiple a través de su método start()
.
¿Por qué no es prudente comparar?
Porque los necesitamos para subprocesos múltiples.
Para Multi-threading necesitamos dos cosas:
- Algo que puede correr dentro de un hilo (Runnable).
- Algo que puede iniciar un nuevo hilo (hilo).
Entonces, tanto técnica como teóricamente, ambos son necesarios para iniciar un hilo, uno correrá y otro lo hará correr (como la Wheel and Engine
del vehículo motorizado).
Es por eso que no puede iniciar un hilo con MyRunnable
, debe pasarlo a una instancia de Thread
.
Pero es posible crear y ejecutar un subproceso solo utilizando la class Thread
porque Class Thread
implementa Runnable
por lo que todos sabemos que Thread
también es un Runnable
en Runnable
interior.
Finalmente, Thread
y Runnable
se complementan entre sí para multiproceso, no competidor o reemplazo.
La creación de una interfaz proporciona una separación más clara entre su código y la implementación de subprocesos, por lo que prefiero implementar Runnable en este caso.
Moraleja de la historia:
Hereda solo si desea anular algún comportamiento.
O más bien debería leerse como:
Heredar menos, interactuar más.
No soy un experto, pero se me ocurre una razón para implementar Runnable en lugar de extender Thread: Java solo admite herencia simple, por lo que solo puedes extender una clase.
Edición: Esto originalmente decía "Implementar una interfaz requiere menos recursos". también, pero necesita crear una nueva instancia de Thread de cualquier manera, así que esto estaba mal.
Sí: implementa Runnable
es la forma preferida de hacerlo, IMO. No estás realmente especializando el comportamiento del hilo. Solo le das algo para correr. Eso significa que la composition es el camino filosóficamente "más puro".
En términos prácticos , significa que puede implementar Runnable
y extenderse desde otra clase también.
Si desea implementar o Runnable
cualquier otra clase, la interfaz de Runnable
es más preferible si no desea que ninguna otra clase se extienda o implemente, entonces la clase Thread
es preferible
La diferencia más común es
Cuando extends Thread
clase extends Thread
, después de eso no puede extender ninguna otra clase que requiera. (Como saben, Java no permite heredar más de una clase).
Cuando implements Runnable
, puede guardar un espacio para su clase para extender cualquier otra clase en el futuro o ahora.
Java no admite la herencia múltiple, lo que significa que solo puede extender una clase en Java, así que una vez que extendió la clase Thread perdió su oportunidad y no puede extender o heredar otra clase en Java.
En la programación orientada a objetos, extender una clase generalmente significa agregar nueva funcionalidad, modificar o mejorar los comportamientos. Si no estamos haciendo ninguna modificación en Thread, entonces use la interfaz Runnable.
La interfaz ejecutable representa una tarea que puede ser ejecutada por un solo hilo o por ejecutores o por cualquier otro medio. la separación lógica de Tarea como Ejecutable que Hilo es una buena decisión de diseño.
Separar tarea como Ejecutable significa que podemos reutilizar la tarea y también tenemos libertad para ejecutarla desde diferentes medios. ya que no se puede reiniciar un hilo una vez que se completa. nuevamente Runnable vs Hilo para la tarea, Runnable es el ganador.
El diseñador Java reconoce esto y es por eso que los Ejecutores aceptan Ejecutar como tarea y tienen un hilo de trabajo que ejecuta esas tareas.
La herencia de todos los métodos Thread conlleva una sobrecarga adicional solo para representar una tarea que se puede hacer 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 en Thread vs Runnable que comparta a través de los comentarios. Personalmente uso Runnable over Thread para este escenario y recomiendo usar la interfaz Runnable o Callable en función de sus requisitos.
Sin embargo, la diferencia significativa es.
Cuando extends Thread
clase extends Thread
, cada uno de sus hilos crea un objeto único y se asocia con él. Cuando implements Runnable
, comparte el mismo objeto con varios subprocesos.
Todo el mundo aquí parece pensar que implementar Runnable es el camino a seguir y realmente no estoy en desacuerdo con ellos, pero en mi opinión, también existe un caso para extender Thread, de hecho, en cierto modo lo has demostrado en tu código.
Si implementa Runnable, la clase que implementa Runnable no tiene control sobre el nombre del hilo, es el código de llamada el que puede establecer el nombre del hilo, así:
new Thread(myRunnable,"WhateverNameiFeelLike");
pero si extiendes Thread, puedes administrar esto dentro de la propia clase (como en tu ejemplo, nombras el hilo ''ThreadB''). En este caso usted:
A) podría darle un nombre más útil para propósitos de depuración
B) están obligando a que ese nombre se use para todas las instancias de esa clase (a menos que ignore el hecho de que es un hilo y haga lo anterior como si fuera un Runnable, pero estamos hablando de una convención aquí en cualquier caso, por lo que puede ignoro esa posibilidad que siento).
Incluso podría, por ejemplo, tomar un seguimiento de la pila de su creación y usarlo como el nombre del hilo. Esto puede parecer extraño, pero dependiendo de cómo esté estructurado su código, puede ser muy útil para propósitos de depuración.
Esto puede parecer algo pequeño, pero cuando tiene una aplicación muy compleja con muchos subprocesos y, de repente, "se han detenido" (ya sea por razones de interbloqueo o posiblemente debido a una falla en un protocolo de red que sería menos obvio (u otras razones interminables) y luego obtener un volcado de pila de Java donde todos los subprocesos se denominan ''Thread-1'', ''Thread-2'', ''Thread-3'' no siempre es muy útil (depende de cómo estén los threads estructurado y si es útil saber cuál es cuál simplemente por su seguimiento de pila, no siempre es posible si está utilizando grupos de varios subprocesos que ejecutan el mismo código).
Habiendo dicho que, por supuesto, también podría hacer lo anterior de una manera genérica creando una extensión de la clase de hilo que establece su nombre en un seguimiento de pila de su llamada de creación y luego use eso con sus implementaciones de Runnable en lugar de la clase estándar de Java Thread (vea a continuación) pero además del seguimiento de la pila, podría haber más información específica del contexto que sería útil en el nombre del hilo para la depuración (una referencia a una de las muchas colas o sockets que podría procesar, por ejemplo, en cuyo caso puede preferir extienda Thread específicamente para ese caso para que el compilador lo obligue a usted (u otros que usen sus bibliotecas) a pasar cierta información (por ejemplo, la cola / socket en cuestión) para usar en el nombre).
Este es un ejemplo del subproceso genérico con el seguimiento de la pila de llamadas como su nombre:
public class DebuggableThread extends Thread {
private static String getStackTrace(String name) {
Throwable t= new Throwable("DebuggableThread-"+name);
ByteArrayOutputStream os = new ByteArrayOutputStream();
PrintStream ps = new PrintStream(os);
t.printStackTrace(ps);
return os.toString();
}
public DebuggableThread(String name) {
super(getStackTrace(name));
}
public static void main(String[] args) throws Exception {
System.out.println(new Thread());
System.out.println(new DebuggableThread("MainTest"));
}
}
y aquí hay una muestra de la salida comparando los dos nombres:
Thread[Thread-1,5,main]
Thread[java.lang.Throwable: DebuggableThread-MainTest
at DebuggableThread.getStackTrace(DebuggableThread.java:6)
at DebuggableThread.<init>(DebuggableThread.java:14)
at DebuggableThread.main(DebuggableThread.java:19)
,5,main]
Una cosa que me sorprende aún no se ha mencionado es que la implementación de Runnable
hace que su clase sea más flexible.
Si extiendes el hilo, entonces la acción que estás haciendo siempre estará en un hilo. Sin embargo, si implementas Runnable
no tiene que ser. Puede ejecutarlo en un hilo, o pasarlo a algún tipo de servicio de ejecutor, o simplemente pasarlo como una tarea dentro de una aplicación de un solo hilo (tal vez se ejecute más adelante, pero dentro del mismo hilo). Las opciones son mucho más abiertas si solo usas Runnable
que si te Runnable
a Thread
.
Ya que este es un tema muy popular y las buenas respuestas se extienden y se tratan en gran profundidad, sentí que es justificable compilar las buenas respuestas de los demás en una forma más concisa, para que los recién llegados tengan una visión general sencilla por adelantado:
Por lo general, se extiende una clase para agregar o modificar la funcionalidad. Por lo tanto, si no desea sobrescribir ningún comportamiento de Thread , use Runnable.
De la misma manera, si no necesita heredar métodos de subprocesos, puede hacerlo sin esa sobrecarga utilizando Runnable.
Herencia única : si extiendes Thread no puedes extender desde ninguna otra clase, así que si eso es lo que necesitas hacer, debes usar Runnable.
Es un buen diseño separar la lógica del dominio de los medios técnicos, en ese sentido, es mejor tener una tarea Ejecutable que aísle la tarea de su corredor .
Puede ejecutar el mismo objeto Runnable varias veces , un objeto Thread, sin embargo, solo se puede iniciar una vez. (Quizás la razón, la razón por la cual los Ejecutores aceptan Runnables, pero no Hilos).
Si desarrolla su tarea como Runnable, tiene toda la flexibilidad para usarla ahora y en el futuro . Puede ejecutarse simultáneamente mediante Ejecutores, pero también mediante Subproceso. Y también podría usar / llamarlo de forma no simultánea dentro del mismo hilo al igual que cualquier otro tipo / objeto común.
Esto también facilita la separación de los aspectos de lógica de tareas y concurrencia en sus pruebas unitarias .
Si está interesado en esta pregunta, también podría estar interesado en la diferencia entre Callable y Runnable .
Yo diría que hay una tercera vía:
public class Something {
public void justAnotherMethod() { ... }
}
new Thread(new Runnable() {
public void run() {
instanceOfSomething.justAnotherMethod();
}
}).start();
Tal vez esto esté influenciado un poco por mi uso intensivo reciente de Javascript y Actionscript 3, pero de esta manera su clase no necesita implementar una interfaz bastante vaga como Runnable
.
tl; dr: implementa Runnable es mejor. Sin embargo, la advertencia es importante.
En general, recomendaría usar algo como Runnable
lugar de Thread
porque le permite mantener su trabajo acoplado libremente con su elección de concurrencia. Por ejemplo, si usa un Runnable
y luego decide que esto no requiere su propio Thread
, puede llamar a threadA.run ().
Advertencia: por aquí, desaliento fuertemente el uso de hilos crudos. Prefiero el uso de Callables y FutureTasks (desde el javadoc: "Un cálculo asíncrono cancelable"). La integración de los 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 las pilas de subprocesos sin procesar.
Seguimiento: hay un constructor FutureTask
que le permite usar Runnables (si eso es lo que le resulta más cómodo) y aún así obtener el beneficio de las modernas herramientas de concurrencia. Para citar el javadoc:
Si no necesita un resultado en particular, considere usar construcciones del formulario:
Future<?> f = new FutureTask<Object>(runnable, null)
Por lo tanto, 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 un ThreadPoolExecutor . Puede usar el método de execute 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 se convertiría en algo similar a lo siguiente (utilizando el método de fábrica Executors.newCachedThreadPool() ):
ExecutorService es = Executors.newCachedThreadPool();
es.execute(new ThreadA());
Encuentro que es más útil usar Runnable por todas las razones mencionadas, pero a veces me gusta extender Thread para poder crear mi propio método de detención de threads y llamarlo directamente al thread que he creado.
Java no admite la herencia múltiple, por lo que si amplía la clase Thread, no se ampliará ninguna otra clase.
Por ejemplo: si creas un applet, debe extender la clase de Applet, por lo que aquí la única forma de crear un hilo es mediante la implementación de la interfaz Runnable.
Si no me equivoco, es más o menos similar a
¿Cuál es la diferencia entre una interfaz y una clase abstracta?
amplia establece la relación " Is A " y la interfaz proporciona " Tiene una " capacidad.
Prefiere implementos Runnable :
- Si no tiene que extender la clase Thread y modificar la implementación predeterminada de Thread API
- Si está ejecutando un comando de fuego y olvido
- Si ya estás extendiendo otra clase.
Prefiere " extiende Hilo ":
- Si tiene que anular cualquiera de estos métodos Thread como se indica en la página de documentación de Oracle
En general, no es necesario anular el comportamiento de Hilo. Por lo tanto, los implementos Runnable se prefieren la mayoría de las veces.
En una nota diferente, el uso avanzado ExecutorService
o ThreadPoolExecutorService
API proporciona más flexibilidad y control.
Echa un vistazo a esta pregunta SE:
Si usa Runnable, puede guardar el espacio para extenderlo a cualquiera de sus otras clases.
Una diferencia entre la implementación de Runnable y la extensión de Thread es que al extender Thread, cada uno de sus threads tiene un objeto único asociado, mientras que la implementación de Runnable, muchos threads pueden compartir la misma instancia de objeto.
Una clase que implementa Runnable no es un hilo y solo una clase. Para que un Runnable sea ejecutado por un Thread, debe crear una instancia de Thread y pasar la instancia de Runnable como destino.
En la mayoría de los casos, la interfaz Runnable se debe usar si solo planea anular el método run () y no otros métodos Thread. Esto es importante porque las clases no deben clasificarse a menos que el programador intente modificar o mejorar el comportamiento fundamental de la clase.
Cuando existe la necesidad de extender una superclase, implementar la interfaz Runnable es más apropiado que usar la clase Thread. Porque podemos extender otra clase mientras implementamos la interfaz Runnable para hacer un hilo. Pero si solo extendemos la clase Thread no podemos heredar de ninguna otra clase.
¿Podemos volver a visitar la razón básica por la que queríamos que nuestra clase se comportara como un Thread
? No hay ninguna razón en absoluto, solo queríamos ejecutar una tarea, lo más probable es que en un modo asíncrono, lo que precisamente significa que la ejecución de la tarea debe derivarse de nuestro hilo principal y el hilo principal si finaliza pronto, puede o no puede esperar para el camino ramificado (tarea).
Si este es todo el propósito, entonces ¿dónde veo la necesidad de un hilo especializado? Esto puede lograrse recogiendo un subproceso RAW del grupo de subprocesos del sistema y asignándole nuestra tarea (puede ser una instancia de nuestra clase) y eso es todo.
Así que obedezcamos el concepto OOP y escribamos una clase del tipo que necesitamos. Hay muchas maneras de hacer las cosas, hacerlo de la manera correcta importa.
Necesitamos una tarea, así que escriba una definición de tarea que se pueda ejecutar en un Thread. Entonces usa Runnable.
Recordar siempre implements
se utiliza especialmente para impartir un comportamiento y extends
se utiliza para impartir una característica / propiedad.
No queremos la propiedad del hilo, en lugar de eso queremos que nuestra clase se comporte como una tarea que se puede ejecutar.
Agregando mis dos centavos aquí - Siempre siempre que sea posible implements Runnable
. A continuación hay dos advertencias sobre por qué no debe usar extends Thread
s
Idealmente, nunca deberías extender la clase Hilo; la
Thread
clase debe hacersefinal
. Al menos sus métodos comothread.getId()
. Vea this discusión para un error relacionado con extenderThread
s.Aquellos a los que les gusta resolver rompecabezas pueden ver otro efecto secundario de extender Thread. El código a continuación imprimirá un código inalcanzable cuando nadie los notifique.
Por favor, consulte http://pastebin.com/BjKNNs2G .
public class WaitPuzzle {
public static void main(String[] args) throws InterruptedException {
DoNothing doNothing = new DoNothing();
new WaitForever(doNothing).start();
new WaitForever(doNothing).start();
new WaitForever(doNothing).start();
Thread.sleep(100);
doNothing.start();
while(true) {
Thread.sleep(10);
}
}
static class WaitForever extends Thread {
private DoNothing doNothing;
public WaitForever(DoNothing doNothing) {
this.doNothing = doNothing;
}
@Override
public void run() {
synchronized (doNothing) {
try {
doNothing.wait(); // will wait forever here as nobody notifies here
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Unreachable Code");
}
}
}
static class DoNothing extends Thread {
@Override
public void run() {
System.out.println("Do Nothing ");
}
}
}
Diferencia entre Thread y Runnable. Si estamos creando Thread usando la clase Thread, entonces el número de thread es igual al número de objeto que creamos. Si estamos creando subprocesos implementando la interfaz ejecutable, podemos usar un solo objeto para crear varios subprocesos. Por lo tanto, un solo subproceso lo comparte. Por lo tanto, se necesitará menos memoria.
Dependiendo del requerimiento si nuestros datos no son sensibles. Por lo tanto, se puede compartir entre varios subprocesos que podemos utilizar interfaz Runnable.
Esa es la S de SOLID : responsabilidad única.
Un hilo encarna el contexto en ejecución (como en el contexto de ejecución: marco de pila, id de hilo, etc.) de la ejecución asíncrona de un fragmento de código. Esa pieza de código idealmente debería ser la misma implementación, ya sea síncrona o asíncrona .
Si los agrupa en una implementación, le da al objeto resultante dos causas de cambio no relacionadas :
- manejo de subprocesos en su aplicación (es decir, consultar y modificar el contexto de ejecución)
- Algoritmo implementado por la pieza de código (la parte ejecutable)
Si el lenguaje que usa admite clases parciales o herencia múltiple, entonces puede segregar cada causa en su propia súper clase, pero se reduce a lo mismo que componer los dos objetos, ya que sus conjuntos de características no se superponen. Eso es para la teoría.
En la práctica, en términos generales, un programa no necesita tener más complejidad de la necesaria. Si tiene un subproceso trabajando en una tarea específica, sin cambiar esa tarea, es probable que no tenga sentido que las tareas se separen de las clases y su código sea más sencillo.
En el contexto de Java , dado que la instalación ya está allí , probablemente sea más fácil comenzar directamente con Runnable
clases independientes y pasar sus instancias a Thread
(o Executor
) instancias. Una vez que se utiliza para ese patrón, no es más difícil de usar (o incluso leer) que el caso de hilo ejecutable simple.
Esto se discute en el tutorial de Definición e inicio de un hilo de Oracle :
¿Cuál de estos modismos deberías usar? El primer idioma, que emplea un objeto Runnable, es más general, porque el objeto Runnable puede subclasificar una clase distinta de Thread. El segundo idioma es más fácil de usar en aplicaciones simples, pero está limitado por el hecho de que su clase de tarea debe ser un descendiente de Thread. Esta lección se enfoca en el primer enfoque, que separa la tarea Ejecutable del objeto Thread que ejecuta la tarea. Este enfoque no solo es más flexible, sino que también es aplicable a las API de administración de subprocesos de alto nivel que se tratan más adelante.
En otras palabras, la implementación Runnable
funcionará en escenarios en los que su clase amplíe una clase distinta de Thread
. Java no soporta herencia múltiple. Además, la extensión Thread
no será posible cuando se utilicen algunas de las API de administración de subprocesos de alto nivel. El único escenario en el que Thread
es preferible extenderse es en una aplicación pequeña que no estará sujeta a actualizaciones en el futuro. Casi siempre es mejor implementarlo, Runnable
ya que es más flexible a medida que su proyecto crece. Un cambio de diseño no tendrá un gran impacto, ya que puede implementar muchas interfaces en java, pero solo extender una clase.
La separación de la clase Thread de la implementación de Runnable también evita posibles problemas de sincronización entre el thread y el método run (). Un Runnable separado generalmente proporciona una mayor flexibilidad en la forma en que se hace referencia y se ejecuta el código ejecutable.
Sí, si llama a la llamada ThreadA, entonces no necesita llamar al método de inicio y el método de ejecución es llamada después de la clase ThreadA. Pero si usa la llamada ThreadB, entonces necesita el hilo de inicio para el método de ejecución de llamada. Si tienes más ayuda, contéstame.
Una de las razones por las que querría implementar una interfaz en lugar de extender una clase base es que ya está extendiendo alguna otra clase. Solo puede extender una clase, pero puede implementar cualquier número de interfaces.
Si extiendes Thread, básicamente estás impidiendo que tu lógica sea ejecutada por cualquier otro hilo que no sea ''this''. Si solo quieres un hilo para ejecutar tu lógica, es mejor simplemente implementar Runnable.