A while ago I joined a project which had some old school (naive) approaches to concurrent processing in an object oriented language.
In these enlightened times I automatically double check whenever I see a Java class that has been declared with "extends Thread" (not just because some former colleagues regarded the extends keyword as blasphemy).
I've read enough to know to prefer implementing an interface rather than extending a concrete class. In this case the appropriate Interface is Runnable, without even delving into the excellent java.util.concurrent APIs.
For this most recent project I have gradually eliminated all "extends Thread", sometimes by simply implementing Runnable, and sometimes by introducing a TimerTask.
Today I have been troubleshooting some problems which our Java application has when interacting with native code (compiled C++ libraries for interacting with hardware). As part of this exercise the application needed to be overhauled to ensure that all threads that are started are correctly shut down when Tomcat is shutdown.
jvisualvm proved to be a useful tool once again for inspecting which threads were not terminating when I ran the application on my laptop.
Like practically everything else in programming, assigning a useful name to each and every Thread and Timer made it so much easier to trace.
Remember the @PreDestroy annotation is your friend.