线程的生命周期分为新建(New)、就绪(Runnable)、运行(Running)、阻塞(Blocked)和死亡(Dead)这 5 种状态。在系统运行过程当中不断会有新的线程被创建,旧的线程在执行完毕后被清理,线程在排队获取共享资源或者锁时将被阻塞,因此运行中线程就是在就绪、阻塞、运行状态之间来回切换。线程的具体状态转换流程如下图所示:

其执行流程:
新建状态 New
在 Java 当中使用 new 关键字创建了一个线程,新创建的线程将处于新建状态。在创建线程的时候主要是为线程分配内存并初始化其成员变量的值。
就绪状态 Runnable
新建的线程对象在调用 start 方法之后将转为就绪状态。此时 JVM 完成了方法调用栈和程序计数器的创建,等待该线程的调度和运行。
运行状态 Running
就绪状态的线程在竞争到 CPU 的使用权并开始执行 run() 方法的线程执行体时,会转为运行状态,处于运行状态的线程的主要任务就是执行 run() 方法中的逻辑代码。
阻塞状态 Blocked
运行中的线程会主动或被动地抛弃 CPU 的使用权并暂停运行,此时该线程将转为阻塞状态,直到再次进入可运行状态,才有机会再次竞争到 CPU 使用权并转为运行状态。阻塞的状态可以分为以下三种:
线程死亡 Dead
线程在以下三种方式结束后转换为死亡状态
线程正常结束:run 方法或 call 方法执行完成
线程异常退出:运行中的线程抛出一个 Error 或未捕获的 Exception,线程异常退出。
手动结束:调用线程对象的 stop 方法手动结束运行中的线程(该方式会瞬间释放线程占用的同步对象锁,导致锁混乱和死锁,不推荐使用)
线程相关的基本方法有 wait、notify、notifyAll、sleep、join、yield 等,这些方法控制线程的运行,并影响线程状态之间的转换。
调用 wait() 方法的线程会进入 WAITING 状态,只有等到其他线程的通知或被中断后才会返回。需要注意的是,在调用 wait() 方法后会释放对象的锁,因此 wait() 方法一般是被使用与同步方法或同步代码块当中。
调用 sleep 方法会导致当前线程休眠。与 wait() 方法不同的是,sleep() 方法不会释放当前占有的锁,会导致线程进入 TIMED-WAITING 状态,而 wait 方法会导致当前线程进入 WAITING 状态。
调用 yield 方法会使当前线程让出(释放)CPU 执行的时间片,与其他线程一起重新竞争 CPU 时间片。在一般情况下,优先级高的线程更有可能竞争到 CPU 时间片,但这不是绝对的,有的操作系统对线程的优先级并不敏感。
interrupt 方法用于向线程发行一个终止通知信号,会影响该线程内部的一个中断标识位,线程本身并不会因为调用了 interrupt 方法而改变状态(阻塞、终止等)。状态的具体变化需要等待接收到中断标识的程序的最终结果来判定。对 interrupt 方法的理解需要注意以下 4 个核心的问题:
public static boolean interrupted() {
return currentThread().isInterrupted(true);
}
public boolean isInterrupted() {
return isInterrupted(false);
}
public class ThreadMethod {
public static void main(String[] args) {
ThreadDemo threadDemo = new ThreadDemo();
threadDemo.start();
threadDemo.interrupt();
}
}
class ThreadDemo extends Thread {
@Override
public void run() {
// 开始时线程中断标识状态为:
System.out.println("初始默认的标识为:" + Thread.currentThread().isInterrupted());
if (Thread.currentThread().isInterrupted()) {
try {
System.out.println("线程sleep睡眠1秒");
sleep(1000);//在这sleep了。就将线程中断改为 false,抛出异常,并立刻结束睡眠
} catch (InterruptedException e) {
System.out.println("出现异常");
// 线程中断异常标识为:
System.out.println("出现异常后标识符为:" + Thread.currentThread().isInterrupted());
Thread.currentThread().interrupt(); //重新设置中断标识
}
}
System.out.println("重置后标识符为:" + Thread.currentThread().isInterrupted());
if (Thread.currentThread().isInterrupted()) {
try {
sleep(1000);//抛出异常
} catch (InterruptedException e) {
System.out.println("因为sleep抛出异常");
e.printStackTrace();
}
}
System.out.println("---------当前线程任务执行完啦---------");
}
}

join 方法是用于等待其他线程终止,如果在当前线程中调用一个线程的 join 方法,则当前线程会转为阻塞状态,等到另一个线程结束,当前线程再由阻塞状态转为就绪状态,等待获取 CPU 的使用权。
在很多情况下,主线程生成并启动子线程,需要等待子线程返回结果并收集和处理再退出,这时就要用到 join 方法。
System.out.println("这个是主线程");
// 生成子线程
ChildThread childThread = new ChildThread();
// 执行子线程
childThread.join();//主线程(当前线程)阻塞等待子线程 childThread 执行线束
System.out.println("子线程join启动后,执行完毕后开始运行主线程");
Object 类有个 notify 方法,用于唤醒在此时对象监视器上等待的一个线程,如果所有线程都在此对象上等待,则会选择唤醒其中一个线程,唤醒时选择是任意的。
我们通常调用其中一个对象的 wait 方法在对象的监视器上等待,直到当前线程放弃此对象上的锁定,才能继续执行被唤醒的线程,被唤醒的线程将以常规方法与在该对象上主动同步的其他线程竞争。类似的方法还有 notifyAll ,用于唤醒在监视器上等待的所有线程。
setDaemon 方法用于定义一个守护线程,也叫做“服务线程”,该线程是后台线程。后台守护线程有一个特性,即为用户线程提供公共服务,在没有用户线程可服务时会自动离开。
守护类线程的优先级较低,用于为系统中的其他对象和线程提供服务。将一个用户线程设置为守护现场的方法是在线程对象创建之前用线程对象的 setDacemon(true) 来设置的。
在后台守护线程中定义线程也是后台守护线程。后台守护线程是 JVM 级别的,比如垃圾回收线程就是一典型的守护线程,在我们的程序中不再有任何线程运行时,程序就不会再产生垃圾,垃圾回收器也就无事可做,所以在回收 JVM 上仅剩的线程时,垃圾回收器线程会自动离开。它始终在低级别的状态下运行,用于实时监控和管理系统中的可回收资源。
守护线程是运行在后台的一种特殊线程,其独立于控制终端并且周期性地执行某种任务或等待处理某些已发生的事件,也就是说,守护线程是不依赖于终端的,但是依赖于 JVM ,与 JVM “同生共死”。在 JVM 中的所有线程都是守护线程时,JVM 就可以退出了,如果还有一个或一个以上的非守护现场,则 JVM 是不会退出的。
这些方法对线程的影响:
