"Implementa Runnable" frente a "extiende Thread" en Java


2119

Desde el tiempo que pasé 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?


57
Gracias por esta pregunta, las respuestas aclararon muchos conceptos erróneos que tenía. Busqué la manera correcta de hacer hilos Java antes de que SO existiera y había mucha información errónea / desactualizada.
James McMahon

55
Hay una razón por la que es posible que desee extender Thread ( pero no lo recomiendo ), puede manejar de forma preventiva interrupt(). Nuevamente, es una idea, podría ser útil en el caso correcto, sin embargo, no lo recomiendo.
bestsss

Consulte también la respuesta, bien explicada: stackoverflow.com/q/5562720/285594

@bestsss, estoy tratando de descifrar lo que podrías decir sobre el manejo de interrupción (). ¿Estás tratando de anular el método?
Bob Cross

8
Sí. Según el código, la clase Thread A puede extender cualquier clase, mientras que la clase Thread B no puede extender ninguna otra clase
mani deepak

Respuestas:


1673

Sí: implementos Runnablees la forma preferida de hacerlo, en mi opinión. Realmente no estás especializando el comportamiento del hilo. Solo le estás dando algo para correr. Eso significa que la composición es el camino filosóficamente "más puro".

En términos prácticos , significa que también puede implementar Runnabley ampliar desde otra clase.


151
Exactamente, bien puesto. ¿Qué comportamiento estamos tratando de sobrescribir en Thread extendiéndolo? Diría que la mayoría de las personas no intentan sobrescribir ningún comportamiento, sino que intentan usar el comportamiento de Thread.
hooknc

87
Como comentario adicional, si crea una instancia de un subproceso y no llama a su método start (), está creando una pérdida de memoria en Java <5 (esto no sucede con Runnables): stackoverflow.com/questions/107823/…
Nacho Coloma

25
Una ventaja menor de Runnable es que, si en ciertas circunstancias no le importa o no desea usar subprocesos, y solo desea ejecutar el código, tiene la opción de simplemente llamar a run (). por ejemplo (muy manual) if (numberCores > 4) myExecutor.excute(myRunnable); else myRunnable.run()
usuario949300

10
@ user949300 también puede hacerlo extends Thready, si no desea enhebrar, ¿por qué implementaría Runnable...
m0skit0

30
Parafraseando a Sierra y Bates, un beneficio clave de implementar Runnable es que estás separando arquitectónicamente el "trabajo" del "corredor".
8bitjunkie

569

tl; dr: implementa Runnable es mejor. Sin embargo, la advertencia es importante

En general, recomendaría usar algo así como en Runnablelugar de Threadporque le permite mantener su trabajo solo libremente con su elección de concurrencia. Por ejemplo, si usa un Runnabley decide más adelante que esto no requiere de hecho Thread, puede llamar a threadA.run ().

Advertencia: Por aquí, desaconsejo el uso de hilos sin procesar. Prefiero el uso de Callables y FutureTasks (del javadoc: "Un cálculo asincrónico cancelable"). La integración de los tiempos de espera, la cancelación adecuada y la agrupación de subprocesos del soporte de concurrencia moderna son mucho más útiles para mí que las pilas de subprocesos sin procesar.

Seguimiento: hay un FutureTaskconstructor que le permite usar Runnables (si eso es con lo que se siente 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 de la forma:

Future<?> f = new FutureTask<Object>(runnable, null)

Entonces, si reemplazamos su runnablecon su threadA, obtenemos lo siguiente:

new FutureTask<Object>(threadA, null)

Otra opción que le permite estar más cerca de Runnables es un ThreadPoolExecutor . Puede usar 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 se convertiría en algo similar a lo siguiente (usando el método de fábrica Executors.newCachedThreadPool () ):

ExecutorService es = Executors.newCachedThreadPool();
es.execute(new ThreadA());

40
Esto es mejor que la respuesta aceptada en mi humilde opinión. Una cosa: el fragmento de código que tiene no cierra al ejecutor y veo millones de preguntas en las que las personas se equivocan, creando un nuevo Ejecutor cada vez que desean generar una tarea. essería mejor como un campo estático (o inyectado), por lo que solo se crea una vez.
artbristol

77
@artbristol, gracias! No estoy en desacuerdo con el nuevo Ejecutor (hacemos lo que usted sugiere en nuestro código). Al escribir la respuesta original, estaba tratando de escribir un código mínimo análogo al fragmento original. Tenemos que esperar que muchos lectores de estas respuestas las usen como puntos de partida. No estoy tratando de escribir un reemplazo para el javadoc. Estoy efectivamente escribiendo material de marketing para ello: si te gusta este método, ¡deberías ver todas las otras cosas geniales que tenemos para ofrecerte ...!
Bob Cross

3
Sé que llego un poco tarde comentando sobre esto, pero tratar FutureTaskdirectamente no es lo que quieres hacer. ExecutorServices va a crear la apropiada Futurepara cuando submitun Runnable/ Callablea ellos. Lo mismo sucede con ScheduledExecutorServices, y ScheduledFuturecuando scheduleun Runnable/ Callable.
Powerlord

3
@Powerlord, mi intención era crear fragmentos de código que coincidieran lo más posible con los OP. Estoy de acuerdo en que el nuevo FutureTask no es óptimo, pero está claro a efectos explicativos.
Bob Cross

257

Moraleja de la historia:

Herede solo si desea anular algún comportamiento.

O más bien debería leerse como:

Herede menos, interactúe más.


1
¡Esta siempre debe ser la pregunta si comienzas a hacer un objeto en ejecución concurrente! ¿Necesitas las funciones de objeto de hilo?
Liebertee

44
Al heredar de Thread, casi siempre se quiere anular el comportamiento del run()método.
Warren Dew

1
No puede anular el comportamiento de a java.lang.Threadanulando el run()método. En ese caso, debes anular el start()método, supongo. Normalmente, simplemente reutiliza el comportamiento de la java.lang.Threadmediante la inyección de su bloque de ejecución en el run()método.
sura2k

La herencia no es solo para anular algunos comportamientos, también es usar comportamientos comunes. Y es todo lo contrario, cuanto más anulaciones, peor jerarquía.
peja

232

Bueno, muchas buenas respuestas, quiero agregar más sobre esto. Esto ayudará a entender Extending v/s Implementing Thread.
Extiende los enlaces de dos archivos de clase muy de cerca y puede causar que algunos códigos sean bastante difíciles de manejar.

Ambos enfoques hacen el mismo trabajo pero ha habido algunas diferencias.
La diferencia más común es

  1. Cuando extiende la clase Thread, después de eso no puede extender ninguna otra clase que necesita. (Como sabes, Java no permite heredar más de una clase).
  2. Cuando implementa Runnable, puede ahorrar espacio para que su clase extienda cualquier otra clase en el futuro o ahora.

Sin embargo, una diferencia significativa entre implementar Runnable y extender 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 lo ayudará a comprender 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 the 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 interfaz Runnable, solo se está creando una instancia de una clase y ha sido compartida por diferentes subprocesos. Por lo tanto, el valor del contador se incrementa para cada acceso de subproceso.

Mientras que, el enfoque de clase de subproceso, debe 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 ocurrirá ningún incremento porque ninguna de las referencias de objeto es igual.

¿Cuándo usar Runnable?
Utilice la interfaz Runnable cuando desee acceder a los mismos recursos del 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 subproceso y solo una clase. Para que un Runnable se convierta en un subproceso, debe crear una instancia de subproceso y pasarse como objetivo.

En la mayoría de los casos, la interfaz Runnable debe usarse si solo planea anular el run()método y no otros métodos Thread. Esto es importante porque las clases no deben subclasificarse a menos que el programador tenga la intención de 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!


40
Su código es evidentemente incorrecto. Quiero decir, hace lo que hace, pero no lo que pretendías mostrar.
zEro

38
Para aclarar: para el caso ejecutable, ha utilizado la misma instancia ImplementsRunnable para iniciar varios subprocesos, mientras que para el caso de subprocesos está creando diferentes instancias ExtendsThread que obviamente conducen al comportamiento que mostró. La segunda mitad de su método principal debería ser: ExtendsThread et = new ExtendsThread(); Thread tc1 = new Thread(et); tc1.start(); Thread.sleep(1000); Thread tc2 = new Thread(et); tc2.start(); Thread.sleep(1000); Thread tc3 = new Thread(et); tc3.start(); ¿Está más claro?
zEro

19
Todavía no entiendo tu intención, pero mi punto era que si creas múltiples instancias de ExtendsThread, todas devolverán 1 (como has mostrado). Puede obtener los mismos resultados para Runnable haciendo lo mismo allí, es decir, creando múltiples instancias de ImplementsRunnable.
zEro

3
@zEro Hola, soy del futuro. Dado que su versión del código también se ha Threadincrementado, ¿la afirmación es by extending Thread, each of your threads has a unique object associated with it, whereas implementing Runnable, many threads can share the same object instanceincorrecta? Si no es así, ¿cuál es el caso que demuestra esto?
Mal Lavadora

44
El código publicado aquí es engañoso y puede provocar dolor de cabeza. Gracias a @zEro por aclararlo un poco.
Nikos

78

Una cosa que me sorprende aún no se ha mencionado es que la implementación Runnablehace que su clase sea más flexible.

Si extiende el hilo, la acción que está haciendo siempre será en un hilo. Sin embargo, si lo implementa Runnableno tiene que 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 sola aplicación de subprocesos (tal vez se ejecute en un momento posterior, pero dentro del mismo subproceso). Las opciones son mucho más abiertas si solo las usas Runnableque si te unes a ellas Thread.


66
Bueno, en realidad también puedes hacer lo mismo con un Threadobjeto porque Thread implements Runnable... ;-) ¡Pero "se siente mejor" haciendo estas cosas con a Runnableque haciéndolas con a Thread!
siegi

77
Es cierto, pero Threadagrega muchas cosas adicionales que no necesita, y en muchos casos no desea. Siempre es mejor implementar la interfaz que coincide con lo que realmente está haciendo.
Herms

74

Si desea implementar o extender cualquier otra clase, la Runnableinterfaz es más preferible; de ​​lo contrario, si no desea que ninguna otra clase extienda o implemente, entonces Threades preferible la clase.

La diferencia más común es

ingrese la descripción de la imagen aquí

Cuando extends Threadclasificas, después de eso no puedes extender ninguna otra clase que requieras. (Como sabes, Java no permite heredar más de una clase).

Cuando lo haga implements Runnable, puede ahorrar espacio para que su clase extienda cualquier otra clase en el futuro o ahora.

  • Java no admite múltiples herencias, lo que significa que solo puede extender una clase en Java, por lo 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 y modificar o mejorar comportamientos. Si no estamos haciendo ninguna modificación en Thread, use la interfaz Runnable en su lugar.

  • La interfaz ejecutable representa una tarea que puede ser ejecutada por Thread o Executors o por cualquier otro medio. Por lo tanto, la separación lógica de Task como Runnable que Thread es una buena decisión de diseño.

  • Separar la tarea como Ejecutable significa que podemos reutilizar la tarea y también tenemos la libertad de ejecutarla desde diferentes medios. ya que no puede reiniciar un subproceso una vez que se completa. nuevamente Runnable vs Thread para la tarea, Runnable es el ganador.

  • El diseñador Java reconoce esto y es por eso que los Ejecutores aceptan Runnable como Tarea y tienen un subproceso de trabajo que ejecuta esa tarea.

  • Heredar todos los métodos de subprocesos son gastos generales adicionales 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 otras diferencias en Thread vs Runnable, compártalas a través de comentarios. Personalmente utilizo Runnable sobre Thread para este escenario y recomiendo usar la interfaz Runnable o Callable según sus requisitos.

Sin embargo, la diferencia significativa es.

Cuando extends Threadclasifica, cada uno de sus hilos crea un objeto único y se asocia con él. Cuando usted implements Runnable, comparte el mismo objeto con múltiples hilos.


73

En realidad, no es aconsejable comparar Runnabley Threadentre sí.

Estos dos tienen una dependencia y relación en subprocesos múltiples al igual que la Wheel and Enginerelación del vehículo de motor.

Yo diría que solo hay una forma de subprocesamiento múltiple con dos pasos. Déjame hacer mi punto.

Ejecutable:
cuando se implementa interface Runnable, significa que está creando algo que está run ableen un hilo diferente. Ahora, crear algo que pueda ejecutarse dentro de un hilo (ejecutable dentro de un hilo), no significa crear un hilo.
Entonces la clase MyRunnableno es más que una clase ordinaria con un void runmétodo. Y sus objetos serán algunos objetos ordinarios con solo un método runque se ejecutará normalmente cuando se llame. (a menos que pasemos el objeto en un hilo).

Thread:
class Thread diría que es una clase muy especial con la capacidad de iniciar un nuevo Thread que realmente permite multi-threading a través de su start()método.

¿Por qué no sabio comparar?
Porque los necesitamos a ambos para subprocesos múltiples.

Para Multi-threading necesitamos dos cosas:

  • Algo que se puede ejecutar dentro de un subproceso (ejecutable).
  • Algo que puede iniciar un nuevo hilo (hilo).

Entonces, técnica y teóricamente, ambos son necesarios para iniciar un hilo, uno correrá y otro lo hará correr (como Wheel and Engineen un vehículo de motor).

Es por eso que no puede iniciar un hilo con el MyRunnableque necesita pasarlo a una instancia de Thread.

Pero es posible crear y ejecutar un subproceso solo mediante el uso de class ThreadClass Thread, Runnablepor lo que todos sabemos que Threadtambién está Runnabledentro.

Finalmente Thready Runnablese complementan entre sí para multihilo no competidor o reemplazo.


3
¡Exactamente! Esta debería ser la respuesta aceptada. Por cierto, creo que la pregunta ha sido editada y ThreadAya no tiene sentido
idelvall

la respuesta aceptada es mucho más delegado gracias por su respuesta @idelvall
Saif

La mejor respuesta! ¡Gracias!
MichaelYe

44

Debe implementar Runnable, pero si está ejecutando en Java 5 o superior, no debe comenzar con, new Threadsino usar un ExecutorService . Para obtener detalles, consulte: Cómo implementar subprocesos simples en Java .


55
No creo que ExecutorService sea tan útil si solo quieres lanzar un solo hilo.
Powerlord

1
Por lo que he aprendido, uno ya no debería comenzar un hilo por su cuenta en general, porque dejarlo al servicio del ejecutor hace que todo sea mucho más controlable (como esperar que el hilo se suspenda). Además, no veo nada en la pregunta que implique que se trata de un solo hilo.
Fabian Steeg

44
¿Cuál es el punto de usar cualquier subproceso múltiple si sabemos a priori que será un solo subproceso? Así que supongamos que tenemos múltiples hilos y esta respuesta es valiosa.
zEro

1
@zEro Estoy bastante seguro de que hay una razón por la que solo hay un hilo de envío de eventos. Dudo que sea el único caso donde es mejor tener un hilo separado pero posiblemente no sea mejor tener múltiples.
porcoesphino

33

No soy un experto, pero puedo pensar en una razón para implementar Runnable en lugar de extender Thread: Java solo admite herencia única, por lo que solo puede extender una clase.

Editar: Esto originalmente decía "Implementar una interfaz requiere menos recursos". también, pero necesita crear una nueva instancia de Thread de cualquier manera, por lo que esto estaba mal.


En runnable no podemos hacer llamadas de red, ¿verdad? Como estoy teniendo android.os.NetworkOnMainThreadException. Pero al usar hilo puedo hacer llamadas de red. Por favor, corríjame si estoy equivocado.
Nabeel Thobani

@NabeelThobani Normal Java no le importa, pero parece que Android sí. Sin embargo, no estoy lo suficientemente familiarizado con Android como para decirlo.
Powerlord

1
@NabeelThobani Por supuesto que puedes. Probablemente no estés creando un hilo con tu Runnable.
m0skit0

20

Yo diría que hay una tercera forma:

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.


38
Esto no es realmente una tercera vía. Todavía estás implementando Runnable, solo haciéndolo de forma anónima.
Don Roby

2
@Don Roby: que es diferente. A menudo es conveniente, y puede usar campos y variables locales finales de la clase / método que lo contiene.
Bart van Heukelom

66
@BartvanHeukelom Es conveniente, pero no diferente. Puede hacerlo con cualquier tipo de clase anidada, es decir, clases internas, clases locales y expresiones lambda.
xehpuk

18

Con el lanzamiento de Java 8, ahora hay una tercera opción.

Runnablees una interfaz funcional , lo que significa que se pueden crear instancias con expresiones lambda o referencias de métodos.

Su ejemplo puede ser reemplazado por:

new Thread(() -> { /* Code here */ }).start()

o si desea utilizar una ExecutorServicereferencia de método y:

executor.execute(runner::run)

Estos no solo son mucho más cortos que sus ejemplos, sino que también vienen con muchas de las ventajas establecidas en otras respuestas Runnablesobre el uso Thread, como la responsabilidad individual y el uso de la composición porque no se está especializando en el comportamiento del hilo. De esta manera, también evita crear una clase adicional si todo lo que necesita es Runnablelo que hace en sus ejemplos.


Esta respuesta necesita explicación. Después de un poco de confusión, concluyo que () -> {}se supone que representa la lógica personalizada que alguien necesita. ¿Entonces sería mejor decirlo () -> { /* Code here */ }?
ToolmakerSteve

17

Instanciar una interfaz proporciona una separación más limpia entre su código y la implementación de subprocesos, por lo que preferiría implementar Runnable en este caso.


12

Todos aquí parecen pensar que implementar Runnable es el camino a seguir y realmente no estoy en desacuerdo con ellos, pero también hay un caso para extender Thread, en mi opinión, de hecho lo has demostrado en tu código.

Si implementa Runnable, entonces la clase que implementa Runnable no tiene control sobre el nombre del subproceso, es el código de llamada que puede establecer el nombre del subproceso, así:

new Thread(myRunnable,"WhateverNameiFeelLike");

pero si extiende Thread, entonces puede administrar esto dentro de la clase en sí (al igual que en su ejemplo, llama al hilo 'ThreadB'). En este caso usted:

A) podría darle un nombre más útil para fines 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 con él como si fuera un Runnable, pero estamos hablando de convenciones aquí, en cualquier caso, también puede ignorar esa posibilidad que siento).

Incluso podría, por ejemplo, tomar un rastro 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 la depuración.

Esto puede parecer algo pequeño, pero cuando tiene una aplicación muy compleja con muchos subprocesos y de repente las cosas 'se han detenido' (ya sea por un punto muerto o posiblemente debido a una falla en un protocolo de red que sería menos obvio, u otras razones interminables), entonces obtener un volcado de pila de Java donde todos los hilos se llaman 'Thread-1', 'Thread-2', 'Thread-3' no siempre es muy útil (depende de cómo sean sus hilos estructurado y si puede decir de manera útil cuál es cuál solo por su seguimiento de la pila, no siempre es posible si está utilizando grupos de múltiples subprocesos que ejecutan el mismo código).

Dicho esto, por supuesto, también puede hacer lo anterior de manera genérica creando una extensión de la clase de subproceso que establece su nombre en un seguimiento de la pila de su llamada de creación y luego usarlo con sus implementaciones Runnable en lugar de la clase estándar de subprocesos de Java (consulte a continuación), pero además del seguimiento de la pila, puede 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 (u otros que usan sus bibliotecas) a pasar cierta información (por ejemplo, la cola / socket en cuestión) para usar en el nombre).

Aquí hay un ejemplo del hilo 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 que compara 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]

¿Cuál es tu punto? No podría usar su código anterior durante la ejecución del subproceso para obtener un seguimiento de la creación de subprocesos (en su lugar, obtendría un nombre simple o, en el mejor de los casos, un seguimiento del inicio del subproceso), pero al subclasificar el subproceso puede hacer exactamente eso y forzar incluso requiere información adicional específica del contexto, lo que le da una comprensión más concreta de exactamente qué hilo puede estar teniendo un problema.
AntonyM

8
Mi punto es que "si implementa Runnable, entonces la clase que implementa Runnable no tiene control sobre el nombre del hilo ..." es evidentemente falsa. Una implementación de clase Runnablepuede controlar el nombre del hilo, ya que el hilo que ejecuta el código es, por definición, el hilo actual (y cualquier código que pase las comprobaciones de seguridad tiene control sobre los nombres de los hilos). Teniendo en cuenta que dedicas la mitad de tu publicación a "¡Dios mío, qué pasa con los nombres de los hilos!", Eso parece un gran problema.
cHao

El nombre del hilo? Nada te impide extender la clase de hilo también.
RichieHH

11

Ejecutable porque:

  • Deja más flexibilidad para la implementación Runnable para extender otra clase
  • Separa el código de la ejecución.
  • Le permite ejecutar su ejecutable desde un grupo de subprocesos, el subproceso de eventos o de cualquier otra manera en el futuro.

Incluso si no necesita nada de esto ahora, puede que lo haga en el futuro. Como no hay ningún beneficio en anular Thread, Runnable es una mejor solución.


11

Dado que este es un tema muy popular y las buenas respuestas se extienden por todas partes y se tratan con gran profundidad, sentí que es justificable recopilar las buenas respuestas de los demás en una forma más concisa, por lo que los recién llegados tienen una visión general fácil por adelantado:

  1. Por lo general, extiende una clase para agregar o modificar la funcionalidad. Por lo tanto, si usted no quiere a sobrescribir cualquier comportamiento del hilo , a continuación, utilizar Ejecutable.

  2. En la misma luz, si no es necesario para heredar los métodos de rosca, se puede hacer sin que sobrecarga utilizando Ejecutable.

  3. Herencia única : si extiende Thread no puede extenderlo desde ninguna otra clase, por lo que si eso es lo que necesita hacer, debe usar Runnable.

  4. Es un buen diseño separar la lógica de dominio de los medios técnicos, en ese sentido es mejor tener una tarea Ejecutable que aísle su tarea de su corredor .

  5. Puede ejecutar el mismo objeto Runnable varias veces , sin embargo, un objeto Thread solo se puede iniciar una vez. (Tal vez la razón por la que los Ejecutores aceptan Runnables, pero no Threads).

  6. Si desarrolla su tarea como Runnable, tiene toda la flexibilidad de cómo usarla ahora y en el futuro . Puede ejecutarlo simultáneamente a través de Ejecutores, pero también a través de Thread. Y aún podría usarlo / llamarlo de manera no simultánea dentro del mismo hilo al igual que cualquier otro tipo / objeto ordinario.

  7. Esto hace que sea también más fácil de separar las tareas de lógica y de concurrencia aspectos en sus pruebas de unidad .

  8. Si está interesado en esta pregunta, también podría estar interesado en la diferencia entre Callable y Runnable .


@Pino Sí, el hilo en sí también es un Runnable. Sin embargo, si lo extiende para usarlo solo como Runnable, ¿cuál es el punto? ¿Por qué no usar un Runnable simple sin todo el equipaje? Entonces, diría que si extiendes Thread, también lo ejecutarías usando su método de inicio, que solo se puede usar una vez. Ese es el punto que Nidhish-Krishnan quería hacer en su respuesta. Tenga en cuenta que la mía es solo una compilación o un breve resumen de otras respuestas aquí.
Jörg

11

Las diferencias entre el subproceso extendido y la ejecución ejecutable son:

ingrese la descripción de la imagen aquí


8

Esto se discute en el tutorial de Oracle Defining and Beginning Thread :

¿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 que no sea 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 descendiente de Thread. Esta lección se centra en el primer enfoque, que separa la tarea Runnable del objeto Thread que ejecuta la tarea. Este enfoque no solo es más flexible, sino que 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 Runnablefuncionará en escenarios en los que su clase extiende una clase que no sea Thread. Java no admite herencia múltiple. Además, la extensión Threadno será posible cuando se utilicen algunas de las API de administración de subprocesos de alto nivel. El único escenario en el que Threades preferible extender es en una pequeña aplicación que no estará sujeta a actualizaciones en el futuro. Casi siempre es mejor implementar, Runnableya que es más flexible a medida que su proyecto crece. Un cambio de diseño no tendrá un impacto importante ya que puede implementar muchas interfaces en Java, pero solo extender una clase.


8

La explicación más simple sería mediante la implementación de Runnableque podemos asignar el mismo objeto a múltiples subprocesos y cada uno Threadcomparte los mismos estados y comportamiento del objeto.

Por ejemplo, supongamos que hay dos subprocesos, el subproceso1 pone un número entero en una matriz y el subproceso2 toma enteros de la matriz cuando se llena la matriz. Tenga en cuenta que para que thread2 funcione, necesita saber el estado de la matriz, si thread1 lo ha llenado o no.

La implementación le Runnablepermite tener esta flexibilidad para compartir el objeto, mientras extends Threadque le permite crear nuevos objetos para cada subproceso, por lo tanto, cualquier actualización realizada por thread1 se pierde en thread2.


7

Si no me equivoco, es más o menos similar a

¿Cuál es la diferencia entre una interfaz y una clase abstracta?

extiende establece la relación " Es A " y la interfaz proporciona la capacidad " Tiene una ".

Prefiere implementos Runnable :

  1. Si no tiene que extender la clase Thread y modificar la implementación predeterminada de la API Thread
  2. Si está ejecutando un comando disparar y olvidar
  3. Si ya estás extendiendo otra clase

Prefiere " hilo extendido ":

  1. Si tiene que anular cualquiera de estos métodos de subprocesos como se enumeran en la página de documentación de Oracle

En general, no es necesario anular el comportamiento del subproceso. Por lo tanto, los implementos Runnable se prefieren la mayoría de las veces.

En una nota diferente, el uso de avanzado ExecutorServiceo ThreadPoolExecutorServiceAPI proporciona más flexibilidad y control.

Echa un vistazo a esta pregunta SE:

ExecutorService vs Casual Thread Spawner


5

Separar la clase Thread de la implementación Runnable también evita posibles problemas de sincronización entre el thread y el método run (). Un Runnable separado generalmente brinda mayor flexibilidad en la forma en que se hace referencia y se ejecuta el código ejecutable.


5

Esa es la S de SÓLIDO : responsabilidad única.

Un subproceso encarna el contexto de ejecución (como en el contexto de ejecución: marco de pila, id de subproceso, etc.) de la ejecución asincrónica de un fragmento de código. Ese código idealmente debería ser la misma implementación, ya sea síncrona o asíncrona .

Si los agrupa en una sola implementación, le da al objeto resultante dos causas de cambio no relacionadas :

  1. manejo de subprocesos en su aplicación (es decir, consultar y modificar el contexto de ejecución)
  2. algoritmo implementado por el fragmento de código (la parte ejecutable)

Si el lenguaje que usa admite clases parciales o herencia múltiple, puede segregar cada causa en su propia superclase, 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 general, un programa no necesita llevar más complejidad de la necesaria. Si tiene un hilo trabajando en una tarea específica, sin cambiar esa tarea, probablemente no tenga sentido hacer que las tareas separen las clases, y su código sigue siendo más simple.

En el contexto de Java , dado que la instalación ya está allí , probablemente sea más fácil comenzar directamente con Runnableclases independientes y pasar sus instancias a Thread(o Executor) instancias. Una vez acostumbrado a ese patrón, no es más difícil de usar (o incluso leer) que el caso de subproceso ejecutable simple.


5

Una razón por la que desea 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 cantidad de interfaces.

Si extiende Thread, básicamente está evitando que su lógica sea ejecutada por cualquier otro hilo que no sea 'this'. Si solo desea que un hilo ejecute su lógica, es mejor implementar Runnable.


Sí, al implementar la interfaz Runnable puedes implementar tu propia lógica al extender cualquier clase, es por eso que Runnable es más preferido que la clase Thread.
Akash5288

5

si usa runnable, puede ahorrar espacio para extenderlo a cualquiera de su otra clase.


5

¿Podemos volver a visitar la razón básica por la que queríamos que nuestra clase se comportara como una Thread? No hay ninguna razón en absoluto, solo queríamos ejecutar una tarea, muy probablemente en un modo asíncrono, lo que significa que la ejecución de la tarea debe ramificarse desde nuestro hilo principal y el hilo principal si termina antes, puede o no esperar para la ruta ramificada (tarea).

Si este es todo el propósito, entonces, ¿dónde veo la necesidad de un hilo especializado? Esto se puede lograr recogiendo un subproceso RAW del conjunto 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 de OOP y escriba una clase del tipo que necesitamos. Hay muchas maneras de hacer las cosas, hacerlo de la manera correcta es importante.

Necesitamos una tarea, así que escriba una definición de tarea que pueda ejecutarse en un subproceso. Entonces usa Runnable.

Recordar siempre implementsse usa especialmente para impartir un comportamiento y extendsse usa para impartir una característica / propiedad.

No queremos la propiedad del hilo, en cambio queremos que nuestra clase se comporte como una tarea que se puede ejecutar.


4

Sí, si llama a ThreadA call, entonces no necesita llamar al método de inicio y el método de ejecución es llamar después de llamar solo a la clase ThreadA. Pero si usa la llamada ThreadB, entonces necesita el hilo de inicio para el método de ejecución de la llamada. Si tienes más ayuda, respóndeme.


4

Creo 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 hilos y llamarlo directamente en el hilo que he creado.


4

Java no admite herencia múltiple, por lo que si extiende la clase Thread, no se extenderá ninguna otra clase.

Por ejemplo: si crea un applet, debe extender la clase Applet, por lo que aquí la única forma de crear un subproceso es mediante la implementación de la interfaz Runnable


4

Runnablees una interfaz, mientras que Threades una clase que implementa esta interfaz. Desde el punto de vista del diseño, debe haber una separación clara entre cómo se define una tarea y cómo se ejecuta. El primero es responsabilidad de una Runnalbeimplementación, y el segundo es trabajo de la Threadclase. En la mayoría de los casos, la implementación Runnablees el camino correcto a seguir.


4

Diferencia entre Thread y runnable. Si estamos creando Thread usando la clase Thread, entonces Número de hilos igual al número de objetos que creamos. Si estamos creando un subproceso implementando la interfaz ejecutable, entonces podemos usar un solo objeto para crear múltiples subprocesos, por lo que un solo objeto es compartido por múltiples subprocesos, por lo que tomará menos memoria

Entonces, dependiendo del requisito, si nuestros datos no son sensibles. Por lo tanto, se puede compartir entre varios subprocesos, podemos utilizar la interfaz Runnable


4

Agregar mis dos centavos aquí: siempre que sea posible usarlo implements Runnable. A continuación hay dos advertencias sobre por qué no debe usar extends Threads

  1. Idealmente, nunca debe extender la clase Thread; La Threadclase debe hacerse final. Al menos sus métodos como thread.getId(). Vea esta discusión para un error relacionado con la extensión de Threads.

  2. Aquellos a quienes les gusta resolver acertijos pueden ver otro efecto secundario de extender Thread. El siguiente código imprimirá un código inalcanzable cuando nadie los notifique.

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 ");
        }
    } 
}
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.