I know that once a thread gets an ID it will not change until the thread is dead. But why does the ID change when we run the below code with start() and it does not change when we call the run() directly?
Because when you call run() directly you aren't creating a new thread, you're just calling the run() method in the current thread. It's just a normal method, there's nothing stopping you calling it like any other method. Whereas calling start() will create a new thread and execute run() in that.