Hoje em dia se recomenda sempre implementar Runnable, não extender Thread.
Isso porque a classe Thread representa um mecanismo de execução, enquanto a interface mostra o que pode ser executado.
Por exemplo, vamos supor que você tenha uma classe que pode ser iniciada em outra thread. Você pode executa-la assim:
Thread t = new Thread(new SuaClasse());
t.start();
Ok. Mas com o tempo você percebe que sua aplicação cria e destroi muitas threads desse tipo (gasta-se muito tempo para criar e destruir uma Thread) gerando um problema de performance acusado por seu profiler. A solução seria usar um Pool de Threads. Como se faz isso?
//Isso aqui vira uma propriedade
ExecutorService threadExecutor = Executors.newCachedThreadPool();
//E no código mesmo você faz
threadExecutor.execute(new SuaClasse());
Vê? Se SuaClasse fosse uma instancia de Thread, você ainda poderia fazer isso, mas estaria desperdiçando código à toa. Conceitualmente, você também estaria dizendo que “sua classe é um executor de Threads”, o que provavelmente estaria errado. Com a interface Runnable, você diz “Minha classe pode ser executada numa Thread”.
Fique atento que se você fosse implementar o Pool à mão, sua classe de Thread teria uma série de controles que estariam relacionados a Thread em si, não à sua classe. Para você seria melhor criar uma filha de Thread com os controles e, novamente, manter sua classe simplesmente implementando Runnable.
Note que, a partir do Java 5, Runnable não é a única interface que um executor pode disparar em uma nova Thread. Existem também a interface Callable, que é um Runnable que retorna valor.
Você pode fazer:
[code]
public class SomaMultithread implements Callable
{
private int a, b;
public SomaMultithread(int a, int b) {
this.a = a;
this.b = b;
}
public Integer call() {
return a+b;
}[/code]
E executar a sua classe em outra Thread fazendo:
Future<Integer> resultado = threadExecutor.submit(new SomaMultithread(10,20));
System.out.println("O resultado da soma foi:" + resultado.get());