Once upon a time, . . . prior to 1.5, using JFrame's contentPane was the recommended approach. Read the
notes for JFrame 1.4 and compare them to the
notes for JFrame 1.5. Even after 1.5, obtaining the contentPane and adding to it is okay. It's just not necessary. Since 1.5, the add() method was constructed to do the same thing.
So there was nothing wrong with what you were "always taught," however, your implementation was confusing at best.
Swing Timers are important tools to be able to use, so it's great that those are working for you, and there's nothing wrong with that. Swing Timers fire an event that causes an actionListener to run, typically on the same thread as the Swing app, the EDT (ideally). Employing other threads to support a Swing app is a fine thing to do when needed, and those should normally be a SwingWorker subclass.