线程的生命周期(线程状态)

在Java中,一个线程总是存在于以下状态之一。这些状态包括:

  1. 新建状态(New)
  2. 活动状态(Active)
  3. 阻塞/等待状态(Blocked / Waiting)
  4. 有时限的等待状态(Timed Waiting)
  5. 终止状态(Terminated)

不同线程状态的解释

新建状态(New):当创建一个新线程时,它始终处于新建状态。对于新建状态的线程,代码尚未运行,因此还未开始执行。

活动状态(Active):当一个线程调用start()方法时,它从新建状态转移到活动状态。活动状态包含两个子状态:一个是可运行(Runnable)状态,另一个是运行(Running)状态。

  • 可运行(Runnable):一个准备运行的线程会被移动到可运行状态。在可运行状态中,线程可以正在运行,也可能在任何给定的时刻准备运行。线程调度器的职责是为线程提供运行的时间,即将线程移动到运行状态。实现多线程的程序将为每个线程获得固定的时间片。每个线程运行一小段时间,当分配的时间片用完时,线程自愿放弃CPU,以便其他线程也能运行它们的时间片。当发生这种情况时,所有愿意运行、等待轮到它们运行的线程都处于可运行状态。在可运行状态中,有一个线程队列。
  • 运行(Running):当线程获得CPU时,它从可运行状态转移到运行状态。通常,线程状态的最常见变化是从可运行到运行,再从运行返回可运行。

阻塞/等待状态(Blocked / Waiting):当线程在一段时间内处于非活动状态(非永久性)时,它要么处于阻塞状态,要么处于等待状态。

例如,一个线程(假设其名称为A)可能希望从打印机中打印一些数据。然而,同时,另一个线程(假设其名称为B)正在使用打印机打印一些数据。因此,线程A必须等待线程B使用打印机。因此,线程A处于阻塞状态。阻塞状态的线程无法执行任何操作,因此不会消耗中央处理单元(CPU)的任何周期。因此,我们可以说线程A保持空闲状态,直到线程调度器重新激活处于等待或阻塞状态的线程A。

当主线程调用join()方法时,主线程处于等待状态。主线程等待子线程完成其任务。当子线程完成工作时,会发送通知给主线程,主线程再次将线程从等待状态转移到活动状态。

如果有很多线程处于等待或阻塞状态,则线程调度器负责确定选择哪个线程和拒绝哪个线程,选择的线程将获得运行的机会。

有时限的等待状态(Timed Waiting):有时,等待会导致饥饿。例如,一个线程(名称为A)进入了代码的临界区并且不愿意离开该临界区。在这种情况下,另一个线程(名称为B)必须永远等待,导致饥饿现象。为了避免这种情况,线程B被赋予一个有时限的等待状态。因此,线程在等待状态下等待一段特定的时间,而不是永远等待。有时限等待的一个真实例子是在特定线程上调用sleep()方法。sleep()方法将线程置于有时限的等待状态。时间到期后,线程唤醒并从之前离开的地方开始执行。

终止状态(Terminated):线程达到终止状态有以下原因:

  • 当一个线程完成其工作时,它正常退出或终止。
  • 异常终止:发生异常事件(如未处理的异常或分段错误)时发生。

终止的线程意味着线程不再存在于系统中。换句话说,线程已经结束,无法重新启动(杀死后再次活动)已经结束的线程。

下图展示了线程生命周期中涉及的不同状态。

life-cycle-of-a-thread.png

线程状态的实现

在Java中,可以使用Thread.getState()方法获取线程的当前状态。Java的java.lang.Thread.State类提供了枚举常量来表示线程的状态。这些常量包括:

public static final Thread.State NEW  

它表示线程的第一个状态,即NEW状态。

public static final Thread.State RUNNABLE  

它表示可运行状态。这意味着线程正在等待在队列中运行。

public static final Thread.State BLOCKED  

它表示阻塞状态。在该状态下,线程正在等待获取锁。

public static final Thread.State WAITING  

它表示等待状态。当线程调用Object.wait()方法或Thread.join()方法而没有超时时,线程将进入此状态。处于等待状态的线程正在等待另一个线程完成其任务。

public static final Thread.State TIMED_WAITING  

它表示有时限的等待状态。等待状态和有时限等待状态的主要区别在于时间限制。等待状态没有时间限制,而有时限等待状态有时间限制。调用以下方法的线程将进入有时限等待状态:

  • sleep
  • join with timeout
  • wait with timeout
  • parkUntil
  • parkNanos
public static final Thread.State TERMINATED  

它表示线程的最终状态,即终止或已结束。终止的线程表示其已完成执行。

用于演示线程状态的Java程序

以下是一个展示上述线程状态的Java程序。

文件名:ThreadState.java

// ABC类实现接口Runnable   
class ABC implements Runnable  
{  
public void run()  
{  
  
//  try-catch 块    
try  
{  
// 将线程 t2 转移到定时等待状态   
Thread.sleep(100);  
}  
catch (InterruptedException ie)  
{  
ie.printStackTrace();  
}  
  
  
System.out.println("The state of thread t1 while it invoked the method join() on thread t2 -"+ ThreadState.t1.getState());  
  
// try-catch 块   
try  
{  
Thread.sleep(200);  
}  
catch (InterruptedException ie)  
{  
ie.printStackTrace();  
}     
}  
}  
  
// ThreadState类实现接口Runnable  
public class ThreadState implements Runnable  
{  
public static Thread t1;  
public static ThreadState obj;  
  
// 主要方法     
public static void main(String argvs[])  
{  
// 创建类 ThreadState 的对象 
obj = new ThreadState();  
t1 = new Thread(obj);  
  
// 线程 t1 被生成      
// 线程 t1 当前处于 NEW 状态。    
System.out.println("The state of thread t1 after spawning it - " + t1.getState());  
  
// 调用 start() 方法    
// 线程t1    
t1.start();  
  
// 线程 t1 进入 Runnable 状态    
System.out.println("The state of thread t1 after invoking the method start() on it - " + t1.getState());  
}  
  
public void run()  
{  
ABC myObj = new ABC();  
Thread t2 = new Thread(myObj);  
  
// 线程 t2 已创建并且当前处于 NEW 状态。  
System.out.println("The state of thread t2 after spawning it - "+ t2.getState());  
t2.start();  
  
// 线程 t2 进入可运行状态   
System.out.println("the state of thread t2 after calling the method start() on it - " + t2.getState());  
  
// try-catch 块用于程序的流畅运行   
try  
{  
// 将线程 t1 转移到定时等待状态      
Thread.sleep(200);  
}  
catch (InterruptedException ie)  
{  
ie.printStackTrace();  
}  
  
System.out.println("The state of thread t2 after invoking the method sleep() on it - "+ t2.getState() );  
  
// try-catch 块用于程序的流畅运行   
try  
{  
//  等待线程 t2 完成执行    
t2.join();  
}  
catch (InterruptedException ie)  
{  
ie.printStackTrace();  
}  
System.out.println("The state of thread t2 when it has completed it's execution - " + t2.getState());  
}  
  
}  

输出:

The state of thread t1 after spawning it - NEW
The state of thread t1 after invoking the method start() on it - RUNNABLE
The state of thread t2 after spawning it - NEW
the state of thread t2 after calling the method start() on it - RUNNABLE
The state of thread t1 while it invoked the method join() on thread t2 -TIMED_WAITING
The state of thread t2 after invoking the method sleep() on it - TIMED_WAITING
The state of thread t2 when it has completed it's execution - TERMINATED

解释:当我们创建一个新线程时,该线程处于NEW状态。当在线程上调用start()方法时,线程调度器将该线程移动到RUNNABLE状态。当在任何线程实例上调用join()方法时,执行该语句的当前线程必须等待该线程完成执行,即将该线程移动到TERMINATED状态。因此,在最终的打印语句被打印到控制台之前,程序在线程t2上调用join()方法,使线程t1等待,直到线程t2完成执行,因此线程t2进入终止状态。线程t1进入等待状态,因为它正在等待线程t2完成执行,而线程t1在线程t2上调用了join()方法。

标签: java, Java面试题, Java下载, java教程, java技术, Java学习, Java学习教程, Java语言, Java开发, Java入门教程, Java进阶教程, Java高级教程, Java笔试题, Java编程思想