Der größte Unterschied zwischen "extends Thread" und "implements Runnable" ist, dass im ersten Fall eine Klasse deklariert wird, die direkt von der Klasse java.lang.Thread erbt und im zweiten Fall eine Klasse deklariert wird, die java.lang.Runnable implementiert. Ein java.lang.Runnable ist einfach nur ein Interface, welches eine run() Methode ohne Parameter und mit void als Rückgabetype deklariert. Nicht mehr und nicht weniger. Da dieses Interface aber sehr "kompatibel" ist mit dem, was ein Thread macht, nämlich einfach etwas asynchron "auszuführen", ohne ein Ergebnis zurückzuliefern oder einen Parameter zu erwarten, kann man einem Thread bei dessen Erzeugung im Konstruktor halt ein Runnable mitgeben.
Alternativ, wenn man sich dazu entscheidet, von der Klasse java.lang.Thread direkt abzuleiten, kann man auch dessen run() Methode überschreiben, und somit dort drin definieren, was der Thread tun soll (als Alternative zum Übergeben eines Runnable beim Konstruktor).