Use the Executors, as recommended by the others.  However, if you want the fun of doing it yourself, try something like this.  (Take care.  I wrote it in Notepad and there's some Exceptions you'll need to catch even if I got everything else right.  Notepad's poor at catching  coding errors.)  This is more a concept than an actual solution to anything, but the idea could be generally useful.
private ConcurrentLinkedQueue<MyThread>  tQueue =
             new ConcurrentLinkedQueue<MyThread>();
class MyThread  extends Thread  {
    public Runnable  doSomething;
    public void run()  {
        // Do the real work.
        doSomething();
        // Clean up and make MyThread available again.
        tQueue.add( mythread );
        // Might be able to avoid this synch with clever code.
        // (Don't synch if you know no one's waiting.)
        // (But do that later.  Much later.)
        synchronized (tQueue)  {
            // Tell them the queue is no longer empty.
            tQueue.notifyAll();
        }
    }
}
Elsewhere:
// Put ten MyThreads in tQueue.
for (int i = 0; i < 10; i++)  tQueue.add( new MyThread() );
// Main Loop.  Runs ten threads endlessly.
for (;;)  {
    MyThread  t = tQueue.poll();
    if (t == null)  {
        // Queue empty.  Sleep till someone tells us it's not.
        do  {
            // There's a try-catch combo missing here.
            synchonized( tQueue )  { tQueue.wait() };
            t = tQueue.poll();
        }  while (t == null)  break;  // Watch for fake alert!
    }
    t.doSomething = do_some_work;
    t.start();
}
Also, note the clever use of ConcurrentLinkedQueue.  You could use something else like ArrayList or LinkedList, but you'd need to synchronize them.