线程的生命周期
线程生命周期指的是一个线程从被创建到运行到结束的整个过程。一般来说,线程生命周期包括以下几个阶段:
- 新建状态(New):当一个线程被创建时,它处于新建状态。此时系统分配了线程所需的资源,但线程还没有开始运行。
- 就绪状态(Runnable):当线程的 start() 方法被调用时,线程进入就绪状态。此时线程已经准备好了运行,但还没有分配到 CPU 资源。
- 运行状态(Running):当线程获得了 CPU 资源,它就进入运行状态。此时线程开始执行自己的代码,直到遇到阻塞、等待或者被中断等情况。
- 阻塞状态(Blocked):当线程需要等待某个事件发生时,它会进入阻塞状态。例如,当调用 Thread.sleep() 方法或者等待 I/O 完成时,线程就会进入阻塞状态。
- 等待状态(Waiting):当线程等待某个条件满足时,它会进入等待状态。例如,调用 Object.wait() 方法时,线程会等待其他线程调用 notify() 或者 notifyAll() 方法来唤醒它。
- 定时等待状态(Timed Waiting):和等待状态类似,但是可以设置等待的时间。例如,调用 Thread.sleep() 或者带有超时参数的 Object.wait() 方法时,线程会进入定时等待状态。
- 终止状态(Terminated):当线程执行完毕或者出现异常时,它就进入终止状态。
下面是一个简单的示例程序,演示线程的生命周期:
public class ThreadLifecycleDemo { public static void main(String[] args) { // 创建线程对象 Thread thread = new Thread(() -> { System.out.println("线程开始执行"); try { Thread.sleep(1000); // 线程进入定时等待状态 } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("线程执行完毕"); }); // 打印线程状态 System.out.println("线程当前状态:" + thread.getState()); // 启动线程 thread.start(); // 等待线程执行完毕 try { thread.join(); } catch (InterruptedException e) { e.printStackTrace(); } // 打印线程状态 System.out.println("线程当前状态:" + thread.getState()); } }
输出结果如下:
线程当前状态:NEW 线程开始执行 线程执行完毕 线程当前状态:TERMINATED
可以看到,线程会依次进入新建状态、就绪状态、运行状态、定时等待状态和终止状态。在示例程序中,我们使用 join() 方法等待线程执行完毕后再打印线程状态,这样可以确保我们看到的状态是最终状态。
小故事
有一个小故事叫做“小明和他的线程”,它可以帮助我们了解线程的生命周期。
小明是一位程序员,他正在开发一个在线游戏。在游戏中,他需要有一个线程来处理用户的输入。所以,他创建了一个线程,并给它取名叫“输入处理线程”。
第一阶段:创建(New)
当小明创建了输入处理线程,线程就进入了创建阶段。在这个阶段,操作系统为线程分配了必要的资源,例如内存和CPU时间。
第二阶段:就绪(Runnable)
一旦输入处理线程被分配了必要的资源,它就可以进入就绪状态。在就绪状态中,线程准备好运行,但是它还没有被调度为运行状态。此时,操作系统会把线程放到就绪队列中,等待CPU的调度。
第三阶段:运行(Running)
当操作系统将输入处理线程从就绪队列中调度出来,并分配了CPU时间,线程就可以进入运行状态。在运行状态中,线程会执行它的任务,例如处理用户输入。
第四阶段:阻塞(Blocked)
有时候,线程执行任务时会被阻塞,例如等待一个网络请求或者等待一个锁。在这种情况下,线程会进入阻塞状态,直到它的任务可以继续执行。
第五阶段:终止(Terminated)
当线程完成了它的任务或者发生了一个致命错误,线程就会进入终止状态。在终止状态中,线程释放了它所占用的资源,并且不再运行。
在小明的例子中,当输入处理线程完成了处理用户输入的任务,它就会进入终止状态。然后,小明可以重新创建一个新的线程来处理下一个用户输入。