1) Your Thread never responds to the interrupt. Calling the Thread#interrupt() method sets a status flag saying the Thread should be interrupted. Some methods will respond to that interruption (either by throwing an InterruptedException or by doing some other signal specified in the API). Nothing in the code you provided responds to interruption so you must check it yourself. You do this by periodically checking the interrupted status flag (either using the static method Thread.interrupted() or Thread.currentThread().isInterrupted(). They behave differently so read the docs on both to see which is most appropriate to use.) A good place to put such a check would be in the while() condition.
2) In the main() method you are waiting for progressively longer times for each thread. The first thread will be allowed to run for 500ms, the second will be allowed to run for 1000ms (the 500ms main waited for t1 plus 500 more), the third for 1500ms, etc... If you want to make the app work just for 500ms then you should have a Thread.sleep(500) followed by checking if each thread is alive, and if it is interrupt it.
3) When you call the Thread.enumerate() method, you get a hold of all the Threads in the current Thread's ThreadGroup. That would include the main thread and any other threads in the same group as the main thread. Are you sure this is what you want? You don't really know all the Threads which are running, do you want to interrupt unknown threads? What happens when you try to join() the main thread? I am not sure. At very least you will interrupt it and since you never check/reset the main thread's interrupted status the next call to join() will throw an InterruptedException (which you don't log) and bring the main thread to an end without informing other Threads to stop. Perhaps you should make sure your custom threads go into their own ThreadGroup or put them into an Array as you generate them.