Mmmm, I disagree, Paul. Much of the time it makes little difference, especially when the program is simple. But as things get more complex, I think it's advantageous to conceptually separate the task to be done (a Runnable) from the worker doing it (a Thread). This shows up in the new
java.util.concurrent package in JDK 1.5. Among other things, this provides convenient thread pool implementations (or more generally, ExecutorServices) that you can configure indepenently of the Runnable tasks to be executed. Here's a demo:
When using java.util.concurrent, you basically never want to start() a Thread yourself. Instead you obtain an Executor (typically an ExecutorService) and tell it to execute the task for you. The execute(Runnable) method expects, well, a Runnable. You
can pass it a Thread, since a Thread is a Runnable, but that's needlessly confusing IMO - why create a Thread if you're not going to call its start() method, but instead use library which will find some
other Thread to execute run()? What if there are junior programmers on the project - they might see your new Thread subclass and figure they're supposed to call it's start() method themselves, ignoring the nifty thread pool you've set up. It's better IMO to encourage everyone to start thinking of workers and tasks as two separate entities. Define a task to be done using a Runnable; execute it using a Thread (pre 1.5) or Executor (1.5+).
I know 1.5 isn't exactly in widespread use yet, but it will be eventually. And there are other concurrency libraries out there - googling "java ThreadPool" gets multiple libraries that have a class of that name. They all seem to be predicated on the idea that you create a Runnable fist, and use the library to get it to execute somehow. Even outside of java 1.5, there's reason to encourage people to separate tasks from workers.