"Implementa Runnable" vs "estende o Thread" em Java


2119

Desde que tempo passei com threads em Java, encontrei estas duas maneiras de escrever threads:

Com implements Runnable:

public class MyRunnable implements Runnable {
    public void run() {
        //Code
    }
}
//Started with a "new Thread(new MyRunnable()).start()" call

Ou, com extends Thread:

public class MyThread extends Thread {
    public MyThread() {
        super("MyThread");
    }
    public void run() {
        //Code
    }
}
//Started with a "new MyThread().start()" call

Existe alguma diferença significativa nesses dois blocos de código?


57
Obrigado por esta pergunta, as respostas esclareceram muitos equívocos que eu tinha. Examinei a maneira correta de executar threads Java antes da existência do SO e havia muitas informações desinformadas / desatualizadas por aí.
1213 James McMahon

5
há um motivo pelo qual você pode estender o Thread ( mas eu não o recomendo ), você pode lidar preventivamente interrupt(). Novamente, é uma ideia, pode ser útil no caso certo, mas não o recomendo.
bestsss

Por favor, veja também a resposta, bem explicada: stackoverflow.com/q/5562720/285594

@ bestsss, estou tentando decifrar o que você pode dizer sobre como lidar com interrupt (). Você está tentando substituir o método?
Bob Cruz

8
yes.As pelo código, classe Thread A pode estender qualquer classe enquanto classe Thread B não pode estender qualquer outra classe
mani deepak

Respostas:


1673

Sim: implementos Runnableé a maneira preferida de fazer isso, IMO. Você não está realmente especializando o comportamento do thread. Você está apenas dando algo para executar. Isso significa que a composição é o caminho filosoficamente "mais puro" a seguir.

Em termos práticos , significa que você também pode implementar Runnablee estender a partir de outra classe.


151
Exatamente, bem colocado. Que comportamento estamos tentando substituir no Thread, estendendo-o? Eu argumentaria que a maioria das pessoas não está tentando substituir nenhum comportamento, mas tentando usar o comportamento do Thread.
21129 hooknc

87
Como um comentário secundário, se você instanciar um Thread e não chamar o método start (), estará criando um vazamento de memória em Java <5 (isso não acontece com Runnables): stackoverflow.com/questions/107823/…
Nacho Coloma

25
Uma pequena vantagem do Runnable é que, em determinadas circunstâncias, você não se importa ou não deseja usar o threading e apenas deseja executar o código, tem a opção de simplesmente chamar run (). por exemplo (muito manual) if (numberCores > 4) myExecutor.excute(myRunnable); else myRunnable.run()
user949300 6/13

10
@ user949300 você também pode fazer isso com extends Threade se você não quer enfiar por que você iria mesmo implementar Runnable...
m0skit0

30
Parafraseando Sierra e Bates, um dos principais benefícios da implementação do Runnable é que você está separando arquiteticamente o "trabalho" do "corredor".
8bjunkie

569

tl; dr: implementa Runnable é melhor. No entanto, a ressalva é importante

Em geral, eu recomendaria o uso de algo como, Runnablee não Threadporque isso permite que você mantenha seu trabalho apenas vagamente associado à sua escolha de simultaneidade. Por exemplo, se você usar ae Runnabledecidir mais tarde que isso não exige sua própria propriedade Thread, basta chamar threadA.run ().

Advertência: Por aqui, desencorajo fortemente o uso de Threads brutos. Eu prefiro o uso de Callables e FutureTasks (do javadoc: "Uma computação assíncrona cancelável"). A integração de tempos limite, o cancelamento adequado e o pool de threads do suporte a simultaneidade moderno são todos muito mais úteis para mim do que pilhas de Threads brutos.

Acompanhamento: existe um FutureTaskconstrutor que permite usar o Runnables (se é com isso que você se sente mais confortável) e ainda obter o benefício das modernas ferramentas de concorrência. Para citar o javadoc :

Se você não precisar de um resultado específico, considere usar construções do formulário:

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

Portanto, se substituirmos o deles runnablepelo seu threadA, obteremos o seguinte:

new FutureTask<Object>(threadA, null)

Outra opção que permite que você fique mais perto do Runnables é um ThreadPoolExecutor . Você pode usar o método execute para executar um Runnable para executar "a tarefa especificada em algum momento no futuro".

Se você quiser tentar usar um pool de threads, o fragmento de código acima se tornaria algo como o seguinte (usando o método de fábrica Executors.newCachedThreadPool () ):

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

40
Isso é melhor do que a resposta aceita IMHO. Uma coisa: o trecho de código que você possui não fecha o executor e vejo milhões de perguntas em que as pessoas entendem errado, criando um novo Executor toda vez que desejam gerar uma tarefa. esseria melhor como um campo estático (ou injetado) para que seja criado apenas uma vez.
Artbristol

7
@artbristol, obrigado! Não discordo do novo Executor (fazemos o que você sugere em nosso código). Ao escrever a resposta original, eu estava tentando escrever um código mínimo análogo ao fragmento original. Temos que esperar que muitos leitores dessas respostas as usem como pontos de partida. Não estou tentando escrever um substituto para o javadoc. Estou efetivamente escrevendo material de marketing para ele: se você gosta desse método, deve ver todas as outras grandes coisas que temos para oferecer ...!
Bob Cruz

3
Sei que estou um pouco atrasado em comentar isso, mas lidar FutureTaskdiretamente com isso geralmente não é o que você deseja fazer. ExecutorServices irá criar o apropriado Futurepara você quando você submitum Runnable/ Callablea eles. Da mesma forma para se ScheduledExecutorServicee ScheduledFuturequando você schedulea Runnable/ Callable.
precisa saber é o seguinte

3
@Powerlord, minha intenção era criar fragmentos de código que correspondessem aos OP o mais próximo possível. Concordo que o novo FutureTask não é o ideal, mas é claro para fins de explicação.
Bob Cruz

257

Moral da história:

Herdar apenas se você deseja substituir algum comportamento.

Ou melhor, deve ser lido como:

Herdar menos, interagir mais.


1
Essa sempre deve ser a pergunta se você começar a criar um Object em execução simultâneo! Você precisa das funções de objeto de segmento?
Liebertee

4
Ao herdar do Thread, quase sempre se deseja substituir o comportamento do run()método.
Warren Orvalho

1
Você não pode substituir o comportamento de a java.lang.Threadsubstituindo o run()método Nesse caso, você precisa substituir o start()método, eu acho. Normalmente, você apenas reutiliza o comportamento do java.lang.Threadinjetando seu bloco de execução no run()método.
sura2k

A herança não é apenas para substituir algum comportamento, é também usar comportamentos comuns. E é o contrário, quanto mais substituições, pior a hierarquia.
peja

232

Bem, tantas boas respostas, quero acrescentar mais sobre isso. Isso ajudará a entender Extending v/s Implementing Thread.
O Extends vincula dois arquivos de classe muito de perto e pode causar dificuldades para lidar com o código.

Ambas as abordagens fazem o mesmo trabalho, mas houve algumas diferenças.
A diferença mais comum é

  1. Quando você estende a classe Thread, depois disso, você não pode estender nenhuma outra classe necessária. (Como você sabe, Java não permite herdar mais de uma classe).
  2. Ao implementar o Runnable, você pode economizar espaço para sua classe estender qualquer outra classe no futuro ou agora.

Entretanto, uma diferença significativa entre a implementação de Thread Runnable e de extensão é 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.

O exemplo a seguir ajudará você a entender mais 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();
 }
}

Saída do programa acima.

ImplementsRunnable : Counter : 1
ImplementsRunnable : Counter : 2
ImplementsRunnable : Counter : 3
ExtendsThread : Counter : 1
ExtendsThread : Counter : 1
ExtendsThread : Counter : 1

Na abordagem da interface Runnable, apenas uma instância de uma classe está sendo criada e foi compartilhada por diferentes threads. Portanto, o valor do contador é incrementado para cada acesso ao encadeamento.

Considerando que, na abordagem da classe Thread, você deve criar uma instância separada para cada acesso ao thread. Portanto, memória diferente é alocada para todas as instâncias de classe e cada uma possui um contador separado, o valor permanece o mesmo, o que significa que nenhum incremento ocorrerá porque nenhuma referência a objeto é a mesma.

Quando usar o Runnable?
Use a interface Runnable quando desejar acessar os mesmos recursos do grupo de threads. Evite usar a classe Thread aqui, porque a criação de vários objetos consome mais memória e isso se torna uma grande sobrecarga de desempenho.

Uma classe que implementa Runnable não é um thread e apenas uma classe. Para um Runnable se tornar um Thread, você precisa criar uma instância do Thread e passar como o destino.

Na maioria dos casos, a interface Runnable deve ser usada se você planeja substituir o run()método e nenhum outro método Thread. Isso é importante porque as classes não devem ser subclassificadas, a menos que o programador pretenda modificar ou aprimorar o comportamento fundamental da classe.

Quando é necessário estender uma superclasse, implementar a interface Runnable é mais apropriado do que usar a classe Thread. Porque podemos estender outra classe enquanto implementamos a interface Runnable para criar um thread.

Espero que isso ajude!


40
Seu código está evidentemente errado. Quero dizer, faz o que faz, mas não o que você pretendia mostrar.
Zero

38
Para esclarecer: para o caso executável, você usou a mesma instância ImplementsRunnable para iniciar vários threads, enquanto que para o caso Thread você está criando instâncias ExtendsThread diferentes, o que obviamente leva ao comportamento que você mostrou. A segunda metade do seu método principal deve 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(); É mais claro?
Zero

19
Ainda não entendo sua intenção, mas o que quero dizer é que, se você criar várias instâncias do ExtendsThread - todas elas retornarão 1 (como você mostrou). Você pode obter os mesmos resultados para Runnable fazendo a mesma coisa lá, ou seja, criando várias instâncias de ImplementsRunnable.
Zero

3
@zEro Oi, eu sou do futuro. Dado que sua versão do código também está Threadaumentando, a declaração está by extending Thread, each of your threads has a unique object associated with it, whereas implementing Runnable, many threads can share the same object instanceerrada? Caso contrário, qual é o caso que demonstra isso?
Evil Washing Machine

4
O código postado aqui é enganoso e pode causar dor de cabeça. Obrigado ao @zEro por esclarecer um pouco.
Nikos

78

Uma coisa que me surpreende ainda não foi mencionada é que a implementação Runnabletorna sua classe mais flexível.

Se você estender o encadeamento, a ação que você está executando sempre estará em um encadeamento. No entanto, se você implementar Runnable, não precisa ser. Você pode executá-lo em um encadeamento, ou passá-lo para algum tipo de serviço executor, ou apenas distribuí-lo como uma tarefa em um único aplicativo encadeado (talvez para ser executado posteriormente, mas dentro do mesmo encadeamento). As opções são muito mais abertas se você apenas usar do Runnableque se vincular Thread.


6
Bem, você também pode fazer a mesma coisa com um Threadobjeto porque Thread implements Runnable… ;-) Mas "é melhor" fazer essas coisas com a do Runnableque fazê-las com a Thread!
Siegi

7
É verdade, mas Threadadiciona muitas coisas extras que você não precisa e, em muitos casos, não deseja. É sempre melhor implementar a interface que corresponde ao que você está realmente fazendo.
Herms

74

Se você deseja implementar ou estender qualquer outra classe, a Runnableinterface é mais preferível; caso contrário, se você não deseja que outra classe estenda ou implemente, a Threadclasse é preferível.

A diferença mais comum é

insira a descrição da imagem aqui

Quando você extends Threadestuda, depois disso, você não pode estender nenhuma outra classe necessária. (Como você sabe, Java não permite herdar mais de uma classe).

Quando você implements Runnablepode economizar espaço para sua classe estender qualquer outra classe no futuro ou agora.

  • O Java não suporta várias heranças, o que significa que você só pode estender uma classe em Java; assim, ao estender a classe Thread, você perde a chance e não pode estender ou herdar outra classe em Java.

  • Na programação orientada a objetos, estender uma classe geralmente significa adicionar novas funcionalidades e modificar ou melhorar comportamentos. Se não estamos fazendo nenhuma modificação no Thread, use a interface Runnable.

  • A interface executável representa uma tarefa que pode ser executada por Thread ou Executores simples ou por qualquer outro meio. A separação lógica da tarefa como executável que o thread é uma boa decisão de design.

  • Separar a tarefa como Executável significa que podemos reutilizá-la e também temos a liberdade de executá-la de diferentes maneiras. pois você não pode reiniciar um thread depois que ele for concluído. novamente Runnable vs Thread para tarefa, Runnable é vencedor.

  • O designer Java reconhece isso e é por isso que os Executores aceitam Runnable como Task e têm um thread de trabalho que executa essas tarefas.

  • Herdar todos os métodos Thread é uma sobrecarga adicional apenas para representar uma tarefa que pode ser feita facilmente com o Runnable.

Cortesia de javarevisited.blogspot.com

Essas foram algumas das diferenças notáveis ​​entre Thread e Runnable em Java. Se você conhece outras diferenças no Thread vs Runnable, compartilhe-o por meio de comentários. Eu pessoalmente uso o Runnable over Thread para esse cenário e recomenda o uso da interface Runnable ou Callable com base nos seus requisitos.

No entanto, a diferença significativa é.

Quando você extends Threadclassifica, cada segmento cria um objeto exclusivo e se associa a ele. Quando você implements Runnablecompartilha o mesmo objeto em vários segmentos.


73

Na verdade, não é sábio para comparar Runnablee Threaduns com os outros.

Esses dois têm uma dependência e um relacionamento no multi-threading, assim como o Wheel and Enginerelacionamento do veículo a motor.

Eu diria que há apenas uma maneira de multi-threading com duas etapas. Deixe-me fazer o meu ponto.

Executável:
ao implementar interface Runnable, significa que você está criando algo que está run ableem um thread diferente. Agora, criar algo que possa ser executado dentro de um thread (executável dentro de um thread) não significa criar um Thread.
Portanto, a classe MyRunnablenada mais é do que uma classe comum com um void runmétodo. E seus objetos serão alguns objetos comuns, com apenas um método runque será executado normalmente quando chamado. (a menos que passemos o objeto em um thread).

Thread:,
class Thread eu diria Uma classe muito especial com a capacidade de iniciar um novo Thread que, na verdade, permite multi-threading através de seu start()método.

Por que não é sábio comparar?
Porque precisamos de ambos para multiencadeamento.

Para Multi-threading, precisamos de duas coisas:

  • Algo que pode ser executado dentro de um Thread (Runnable).
  • Algo que pode iniciar um novo Thread (Thread).

Então, técnica e teoricamente, os dois são necessários para iniciar um segmento, um será executado e o outro será executado (como Wheel and Engineno veículo a motor).

É por isso que você não pode iniciar um encadeamento com MyRunnablevocê precisa passá-lo para uma instância de Thread.

Mas é possível criar e executar um encadeamento usando apenas class Threadporque o Class Threadimplementa, Runnableassim todos sabemos que Threadtambém é Runnableinterno.

Finalmente Threade Runnablesão complementares entre si para multithreading não concorrente ou substituição.


3
Exatamente! Essa deve ser a resposta aceita. BTW eu acho que a questão foi editado e ThreadAnão tem mais sentido
idelvall

a resposta aceita é muito mais delegar graças para você resposta @idelvall
Saif

A melhor resposta! Obrigado!
MichaelYe

44

Você deve implementar o Runnable, mas se estiver executando no Java 5 ou superior, não deve iniciá-lo, new Threadmas usar um ExecutorService . Para obter detalhes, consulte: Como implementar threading simples em Java .


5
Eu não acho que ExecutorService seria tão útil se você quiser iniciar um único thread.
Powerlord

1
Pelo que aprendi, não se deve mais iniciar um encadeamento sozinho, em geral, porque deixar isso para o serviço do executor torna tudo muito mais controlável (como aguardar a suspensão do encadeamento). Além disso, não vejo nada na pergunta que implique que se trata de um único thread.
1211 Fabian Steeg

4
Qual é o sentido de usar qualquer multiencadeamento, se soubermos anteriormente que será um único encadeamento. Então, vamos assumir que temos vários threads e essa resposta é valiosa.
Zero

1
@zEro Tenho certeza de que há uma razão para haver apenas um Tópico de Despacho de Evento. Duvido que seja o único caso em que seja melhor ter um segmento separado, mas possivelmente não seja melhor ter vários.
Porcoesphino

33

Não sou especialista, mas posso pensar em um motivo para implementar Runnable em vez de estender Thread: Java suporta apenas herança única, portanto, você pode estender apenas uma classe.

Edit: Originalmente dito "A implementação de uma interface requer menos recursos". também, mas você precisa criar uma nova instância do Thread de qualquer maneira, então isso estava errado.


Em executável, não podemos fazer chamadas de rede, é? Como eu estou tendo android.os.NetworkOnMainThreadException. Mas, usando o thread, posso fazer chamadas de rede. Por favor corrija-me se eu estiver errado.
Nabeel Thobani

@NabeelThobani O Java normal não se importa, mas soa como o Android. Eu não estou familiarizado o suficiente com o Android para dizer, no entanto.
Powerlord

1
@NabeelThobani Claro que você pode. Provavelmente você não está criando um Thread com seu Runnable.
M0skit0

20

Eu diria que existe uma terceira maneira:

public class Something {

    public void justAnotherMethod() { ... }

}

new Thread(new Runnable() {
   public void run() {
    instanceOfSomething.justAnotherMethod();
   }
}).start();

Talvez isso tenha sido influenciado um pouco pelo meu recente uso pesado de Javascript e Actionscript 3, mas dessa forma sua classe não precisa implementar uma interface bastante vaga como essa Runnable.


38
Esta não é realmente uma terceira via. Você ainda está implementando o Runnable, apenas fazendo anonimamente.
Don Roby 12/01

2
@ Don Roby: O que é diferente. Geralmente é conveniente, e você pode usar campos e variáveis ​​locais finais da classe / método que o contém.
Bart van Heukelom 13/01

6
@BartvanHeukelom É conveniente, mas não diferente. Você pode fazer isso com qualquer tipo de classe aninhada, ou seja, classes internas, classes locais e expressões lambda.
xehpuk

18

Com o lançamento do Java 8, agora existe uma terceira opção.

Runnableé uma interface funcional , o que significa que instâncias dela podem ser criadas com expressões lambda ou referências de método.

Seu exemplo pode ser substituído por:

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

ou se você deseja usar uma ExecutorServicee uma referência de método:

executor.execute(runner::run)

Estes não são apenas muito mais curtos que os seus exemplos, mas também trazem muitas das vantagens indicadas em outras respostas do uso Runnableexcessivo Thread, como responsabilidade única e uso da composição, porque você não está especializando o comportamento do encadeamento. Dessa maneira, também evita-se a criação de uma classe extra, se tudo o que você precisa é o Runnableque você faz em seus exemplos.


Esta resposta precisa de explicação. Depois de alguma confusão, concluo que () -> {}é suposto representar a lógica personalizada de que alguém precisa? Então seria melhor dizer como () -> { /* Code here */ }?
Página

17

A instanciação de uma interface oferece uma separação mais limpa entre seu código e a implementação de threads, portanto, eu preferiria implementar o Runnable nesse caso.


12

Todos aqui parecem pensar que implementar o Runnable é o caminho a percorrer e eu realmente não discordo deles, mas também há um motivo para estender o Thread na minha opinião, na verdade, você meio que demonstrou isso no seu código.

Se você implementar Runnable, a classe que implementa Runnable não tem controle sobre o nome do encadeamento, é o código de chamada que pode definir o nome do encadeamento, assim:

new Thread(myRunnable,"WhateverNameiFeelLike");

mas se você estender o Thread, poderá gerenciar isso dentro da própria classe (assim como no seu exemplo, você nomeará o segmento 'ThreadB'). Nesse caso, você:

A) pode dar a ele um nome mais útil para fins de depuração

B) estão forçando que esse nome seja usado para todas as instâncias dessa classe (a menos que você ignore o fato de que é um encadeamento e faça o acima com ele como se fosse um Runnable, mas estamos falando de convenção aqui em qualquer caso, portanto, pode ignore essa possibilidade que sinto).

Você pode, por exemplo, pegar um rastreio de pilha de sua criação e usá-lo como o nome do encadeamento. Isso pode parecer estranho, mas dependendo da estrutura do seu código, pode ser muito útil para fins de depuração.

Isso pode parecer uma coisa pequena, mas onde você tem um aplicativo muito complexo com muitos threads e de repente coisas "pararam" (por razões de conflito ou possivelmente por causa de uma falha em um protocolo de rede, seria menos óbvio - ou por outras razões sem fim), obter um despejo de pilha do Java, onde todos os threads são chamados 'Thread-1', 'Thread-2', 'Thread-3' nem sempre é muito útil (depende de como seus threads são estruturado e se é útil saber qual é qual apenas pelo rastreamento da pilha - nem sempre é possível se você estiver usando grupos de vários threads executando o mesmo código).

Dito isto, é claro que você também pode fazer o acima de uma maneira genérica, criando uma extensão da classe thread que define seu nome para um rastreamento de pilha de sua chamada de criação e, em seguida, use isso com suas implementações Runnable em vez da classe Thread Java padrão (veja abaixo), mas, além do rastreamento da pilha, pode haver mais informações específicas do contexto que seriam úteis no nome do encadeamento para depuração (uma referência a uma das muitas filas ou soquetes que ele poderia processar, por exemplo, caso em que você prefere estenda o Thread especificamente para esse caso, para que o compilador force você (ou outras pessoas usando suas bibliotecas) a transmitir determinadas informações (por exemplo, a fila / soquete em questão) para uso no nome).

Aqui está um exemplo do encadeamento genérico com o rastreamento da pilha de chamada como seu nome:

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"));
    }
}

e aqui está uma amostra da saída comparando os dois nomes:

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]

cHao qual é o seu ponto? Não foi possível usar o código acima durante a execução do encadeamento para obter um rastreamento de pilha da criação de encadeamentos (em vez disso, você obteria um nome simples ou, na melhor das hipóteses, um rastreamento de empilhamento do lançamento de encadeamentos), mas subclassificando o encadeamento, você pode fazer exatamente isso e forçar mesmo exigindo mais informações específicas do contexto, fornecendo a você um entendimento mais concreto sobre exatamente qual thread pode estar tendo um problema.
precisa saber é o seguinte

8
O que quero dizer é que "se você implementa o Runnable, a classe que implementa o Runnable não tem controle sobre o nome do encadeamento ..." é evidentemente falso. Uma implementação de classe Runnablepode realmente controlar o nome do encadeamento, pois o encadeamento que executa o código é, por definição, o encadeamento atual (e qualquer código que passa nas verificações de segurança tem controle sobre os nomes dos encadeamentos). Considerando que você dedica metade do seu post para "omg, e quanto aos nomes de tópicos!", Isso parece um grande negócio.
Chao

O nome do segmento? Nada está impedindo que você estenda a classe de threads também.
RichieHH

11

Executável porque:

  • Deixa mais flexibilidade para a implementação Runnable estender outra classe
  • Separa o código da execução
  • Permite executar o seu executável a partir de um Pool de Threads, do thread de eventos ou de qualquer outra forma no futuro.

Mesmo se você não precisar de nada disso agora, poderá no futuro. Como não há benefício em substituir o Thread, o Runnable é uma solução melhor.


11

Como esse é um tópico muito popular e as boas respostas estão espalhadas por toda parte e tratadas com grande profundidade, achei justificável compilar as boas respostas dos outros em uma forma mais concisa, para que os novatos tenham uma visão geral fácil:

  1. Você geralmente estende uma classe para adicionar ou modificar a funcionalidade. Então, se você não quer para substituir qualquer comportamento da linha , em seguida, usar Runnable.

  2. Da mesma forma, se você não precisar herdar métodos de encadeamento, poderá passar sem essa sobrecarga usando Runnable.

  3. Herança única : se você estender o Thread, não poderá estender a partir de nenhuma outra classe; portanto, se é isso que você precisa fazer, use o Runnable.

  4. É um bom design separar a lógica do domínio dos meios técnicos; nesse sentido, é melhor ter uma tarefa Runnable isolando sua tarefa do seu runner .

  5. Você pode executar o mesmo objeto Runnable várias vezes ; um objeto Thread, no entanto, só pode ser iniciado uma vez. (Talvez o motivo pelo qual os Executores aceitem Runnables, mas não os Threads.)

  6. Se você desenvolver sua tarefa como executável, terá toda a flexibilidade de como usá-la agora e no futuro . Você pode executá-lo simultaneamente via Executors, mas também via Thread. E você ainda pode usar / chamá-lo de forma não simultânea no mesmo encadeamento, assim como qualquer outro tipo / objeto comum.

  7. Isso também facilita a separação de aspectos de lógica de tarefa e simultaneidade em seus testes de unidade .

  8. Se você está interessado nesta pergunta, também pode estar interessado na diferença entre Chamada e Runnable .


@Pino Sim, o próprio thread também é um executável. No entanto, se você o estender para usá-lo como um Runnable, qual é o objetivo? Por que não usar apenas um Runnable simples sem toda a bagagem? Então, eu argumentaria que, se você estender o Thread, também o executaria usando o método start, que pode ser usado apenas uma vez. Esse é o ponto que Nidhish-Krishnan queria fazer em sua resposta. Observe que a minha é apenas uma compilação ou um breve resumo de outras respostas aqui.
Jörg

11

As diferenças entre Estender Thread e Implementar Runnable são:

insira a descrição da imagem aqui


8

Isso é discutido no tutorial Definindo e iniciando um encadeamento da Oracle :

Quais desses idiomas você deve usar? O primeiro idioma, que emprega um objeto Runnable, é mais geral, porque o objeto Runnable pode subclassificar uma classe diferente de Thread. O segundo idioma é mais fácil de usar em aplicativos simples, mas é limitado pelo fato de que sua classe de tarefas deve ser descendente do Thread. Esta lição se concentra na primeira abordagem, que separa a tarefa Runnable do objeto Thread que executa a tarefa. Essa abordagem não é apenas mais flexível, mas é aplicável às APIs de gerenciamento de encadeamento de alto nível abordadas posteriormente.

Em outras palavras, a implementação Runnablefuncionará em cenários em que sua classe estende uma classe diferente de Thread. Java não suporta herança múltipla. Além disso, a extensão Threadnão será possível ao usar algumas das APIs de gerenciamento de encadeamento de alto nível. O único cenário em que a extensão Threadé preferível é em um aplicativo pequeno que não estará sujeito a atualizações no futuro. Quase sempre é melhor implementarRunnable lo, pois é mais flexível à medida que seu projeto cresce. Uma mudança de design não terá um grande impacto, pois você pode implementar muitas interfaces em java, mas apenas estender uma classe.


8

A explicação mais simples seria implementando: Runnablepodemos atribuir o mesmo objeto a vários threads e cadaThread compartilha os mesmos estados e comportamento do objeto.

Por exemplo, suponha que haja dois encadeamentos, o encadeamento1 coloca um número inteiro em uma matriz e o encadeamento2 pega números inteiros da matriz quando a matriz é preenchida. Observe que, para que o thread2 funcione, ele precisa conhecer o estado da matriz, se o thread1 o preencheu ou não.

A implementação Runnablepermite que você tenha essa flexibilidade para compartilhar o objeto, enquanto extends Threadfaz com que você crie novos objetos para cada encadeamento, portanto, qualquer atualização feita pelo encadeamento1 é perdida no encadeamento2.


7

Se não estou errado, é mais ou menos parecido com

Qual é a diferença entre uma interface e uma classe abstrata?

estende estabelece relação " É um " e interface fornece capacidade " Tem um ".

Prefere implementa Runnable :

  1. Se você não precisar estender a classe Thread e modificar a implementação padrão da API da Thread
  2. Se você estiver executando um comando de incêndio e esqueça
  3. Se você já está estendendo outra classe

Prefere " estende o segmento ":

  1. Se você precisar substituir qualquer um desses métodos de Thread , conforme listado na página de documentação da Oracle

Geralmente, você não precisa substituir o comportamento do Thread. Então implementa Runnable é o preferido na maioria das vezes.

Em uma observação diferente, o uso de API avançada ExecutorServiceou ThreadPoolExecutorServicefornece mais flexibilidade e controle.

Dê uma olhada nesta pergunta SE:

ExecutorService vs Casual Thread Spawner


5

A separação da classe Thread da implementação Runnable também evita possíveis problemas de sincronização entre o thread e o método run (). Um Runnable separado geralmente oferece maior flexibilidade na maneira como o código executável é referenciado e executado.


5

Esse é o S do SOLID : responsabilidade única.

Um encadeamento incorpora o contexto de execução (como no contexto de execução: quadro de pilha, ID de encadeamento, etc.) da execução assíncrona de um trecho de código. Idealmente, esse trecho de código deve ter a mesma implementação, síncrona ou assíncrona .

Se os agrupar em uma implementação, você fornecerá ao objeto resultante duas causas de mudança não relacionadas :

  1. tratamento de threads em seu aplicativo (ou seja, consultando e modificando o contexto de execução)
  2. algoritmo implementado pelo trecho de código (a parte executável)

Se o idioma que você usa suportar classes parciais ou herança múltipla, você poderá segregar cada causa em sua própria superclasse, mas tudo se resume a compor os dois objetos, já que seus conjuntos de recursos não se sobrepõem. Isso é para a teoria.

Na prática, de um modo geral, um programa não precisa ter mais complexidade do que o necessário. Se você tem um encadeamento trabalhando em uma tarefa específica, sem nunca alterá-la, provavelmente não faz sentido separar as tarefas de classes e seu código permanece mais simples.

No contexto de Java , como o recurso está , é provavelmente mais fácil iniciar diretamente com Runnableclasses independentes e transmitir suas instâncias para Thread(ou Executor) instâncias. Uma vez acostumado a esse padrão, não é mais difícil de usar (ou mesmo ler) do que o simples caso de thread executável.


5

Um motivo pelo qual você deseja implementar uma interface em vez de estender uma classe base é que você já está estendendo alguma outra classe. Você pode estender apenas uma classe, mas pode implementar qualquer número de interfaces.

Se você estender o Thread, estará basicamente impedindo que sua lógica seja executada por qualquer outro thread que não seja 'this'. Se você deseja que apenas um thread execute sua lógica, é melhor implementar o Runnable.


Sim, ao implementar a interface Runnable, você pode implementar sua própria lógica estendendo qualquer classe, é por isso que Runnable é preferível à classe Thread.
precisa saber é o seguinte

5

se você usa runnable, pode economizar espaço para estender a qualquer outra classe.


5

Podemos voltar a visitar a razão básica pela qual queríamos que nossa classe se comportasse como Thread? Não há motivo algum, apenas queríamos executar uma tarefa, provavelmente em modo assíncrono, o que significa precisamente que a execução da tarefa deve se ramificar de nosso encadeamento principal e do encadeamento principal se terminar cedo, pode ou não esperar para o caminho ramificado (tarefa).

Se esse for o objetivo, onde vejo a necessidade de um Thread especializado. Isso pode ser conseguido escolhendo um Thread RAW no Pool de Threads do sistema e atribuindo a ele nossa tarefa (pode ser uma instância da nossa classe) e é isso.

Então, vamos obedecer ao conceito de POO e escrever uma classe do tipo que precisamos. Existem muitas maneiras de fazer as coisas, da maneira certa.

Como precisamos de uma tarefa, escreva uma definição de tarefa que possa ser executada em um Thread. Então use Runnable.

Lembre-se sempre de que implementsé especialmente usado para transmitir um comportamento e extendsé usado para transmitir um recurso / propriedade.

Não queremos a propriedade do encadeamento, mas queremos que nossa classe se comporte como uma tarefa que pode ser executada.


4

Sim, se você chamar a chamada ThreadA, não precisará chamar o método start e o método run é chamar após chamada apenas a classe ThreadA. Mas se usar a chamada ThreadB, será necessário o thread inicial para o método de execução de chamada. Se você tiver mais alguma ajuda, me responda.


4

Acho que é mais útil usar o Runnable por todos os motivos mencionados, mas às vezes gosto de estender o Thread para que eu possa criar meu próprio método de parada de thread e chamá-lo diretamente no thread que eu criei.


4

Java não suporta herança múltipla, portanto, se você estender a classe Thread, nenhuma outra classe será estendida.

Por exemplo: Se você criar um applet, ele deverá estender a classe Applet; portanto, aqui a única maneira de criar encadeamento é implementando a interface Runnable


4

Runnableé uma interface, enquanto Threadé uma classe que implementa essa interface. Do ponto de vista do design, deve haver uma separação clara entre como uma tarefa é definida e entre como é executada. O primeiro é de responsabilidade de uma Runnalbeimplementação e o segundo é o trabalho da Threadclasse. Na maioria dos casos, implementar Runnableé o caminho certo a seguir.


4

Diferença entre Thread e executável. Se estamos criando Thread usando a classe Thread, então Number of thread é igual ao número de objeto que criamos. Se estivermos criando um encadeamento implementando a interface executável, podemos usar um único objeto para criar vários encadeamentos.

Portanto, dependendo do requisito, se nossos dados não forem sensíveis. Portanto, pode ser compartilhado entre vários threads, podemos usar a interface Runnable.


4

Adicionando meus dois centavos aqui - sempre que possível implements Runnable. Abaixo estão duas advertências sobre por que você não deve usar extends Threads

  1. Idealmente, você nunca deve estender a classe Thread; a Threadaula deve ser feita final. Pelo menos seus métodos gostam thread.getId(). Consulte esta discussão para obter um bug relacionado à extensão de Threads.

  2. Quem gosta de resolver quebra-cabeças pode ver outro efeito colateral da extensão do Thread. O código abaixo imprimirá código inacessível quando ninguém os estiver notificando.

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 ");
        }
    } 
}
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.