If You can see the source code of Thread class you can understand that even Thread class itself implements Runnable interface.
http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/java/lang/Thread.java#Thread.run%28%29
public More ...Thread() {
init(null, null, "Thread-" + nextThreadNum(), 0);
}
public More ...Thread(Runnable target) {
init(null, target, "Thread-" + nextThreadNum(), 0);
}
public synchronized void More ...start() {
if (threadStatus != 0)
throw new IllegalThreadStateException();
group.add(this);
start0();
if (stopBeforeStart) {
stop0(throwableFromStop);
}
}
private native void More ...start0();
public void More ...run() {
if (target != null) {
target.run();
}
}
So,
Case:1 i.e. extending Thread class
Here basically you will be overriding the run() method of Thread.class and hence on invoking the start(), the native start0() method creates the new Thread from underlying OS and invokes the overridden run() method.
Case:2 i.e. implementing Runnable Interface
Here you will be setting the Runnable implementation object to target. Hence on invoking the start(), the native start0() method creates the new Thread from underlying OS and invokes the run() method of the Thread.class and as you can see it will call the target.run() i.e. your Runnable implementation Object's run() method.