I'm not sure what to say about your lecturer.
There's almost never any reason to extend Thread. The usual practice in java is to implement Runnable. This Runnable is then passed to a thread or a pool of threads, who perform the work specified in run(). Think of the body of the run() method as a unit of work to be performed by a thread.
Putting the logic in a singleton object with synchronized methods.... this makes no sense at all and is a very bad design. Calling a synchronized method locks on the surrounding object, which means every thread calling synchronized methods on that object blocks all the other threads, even if they're calling different synchronized methods. If your entire simulation is being run through a singleton "Monitor" class, all threads will have to block all other threads every time they call one of the methods. This is horrible for performance and it's a great way to create deadlocks if you have more than one class like this in your program. This whole approach is a giant can of worms.
A properly designed multi-threaded program will generally have a bunch of Runnables that represent units of work. Some class will generate these Runnables and feed them to a pool of threads who will perform them. If these Runnables aren't sharing resources, there's no need for grabbing locks. If Runnables are sharing resources, you figure out a way to represent those resources with a lock object and give out those lock objects to the Runnables so that they can synchronize on them during the run() method.
The goal is to have the threads block one another to the minimum extent possible. This is the exact opposite of the model your instructor provided.
extend
something, you create an "is a" relationship, as indog extends animal
. APerson
is most decidedly not aThread
. Implementing most of the logic in the monitor makes no sense either. I don't think your lecturer knew what he was talking about.