一、Java线程的状态
在Java中,多线程是由Thread的核心概念驱动的。在它们的生命周期中,线程会经历各种状态:
java.lang.Thread类包含一个静态的 State 枚举——它定义了线程的状态。包括以下几种状态:
NEW – 新创建的尚未开始执行的线程
RUNNABLE –正在运行或准备好执行,但它正在等待资源分配
BLOCKED –等待获取监视器锁以进入或重新进入同步块/方法
WAITING –等待其他线程执行特定操作,没有任何时间限制
TIMED_WAITING –等待某个其他线程在指定时间段内执行特定操作
TERMINATED –已完成执行
1、NEW
NEW Thread是已创建但尚未启动的线程。在使用start()方法启动它之前,它会一直保持这种状态。
下面代码显示了一个新创建的处于NEW状态的线程:
Runnable runnable = new NewState();
Thread t = new Thread(runnable);
System.out.println("Show:" + t.getState());
2、RUNNABLE
当我们创建一个新线程并在其上调用start()方法时,它会从NEW状态转移到RUNNABLE状态。处于这种状态的线程要么正在运行,要么准备运行,但它们正在等待系统分配资源。
在多线程环境中,线程调度程序(它是 JVM 的一部分)为每个线程分配固定的时间。所以它会运行一段特定的时间,然后将控制权交给其他RUNNABLE线程。
下面代码最有可能输出RUNNABLE,但是并不能总是保证当执行t.getState()时,它仍将处于RUNNABLE状态。它可能会立即被Thread-Scheduler 调度并可能完成执行。在这种情况下,我们可能会得到不同的输出。
Runnable runnable = new NewState();
Thread t = new Thread(runnable);
t.start();
System.out.println("Show:" + t.getState());
3、BLOCKED
当线程当前不符合运行条件时,它处于BLOCKED状态。它在等待监视器锁定并尝试访问被其他线程锁定的一段代码时进入此状态。
参考下面的代码。
(1)创建了t1和t2两个线程。
(2)t1启动并进入同步的commonResource()方法;这意味着只有一个线程可以访问它;所有其他尝试访问此方法的后续线程将被阻止进一步执行,直到当前线程完成处理。
(3)当t1进入这个方法时,它一直处于一个无限的while循环中;这只是为了模拟密集的计算,使所有其他线程无法进入该方法。
(3)当我们启动t2时,它会尝试进入commonResource()方法,该方法已经被t1 访问,因此,t2将保持在 BLOCKED状态。
public class BlockedState {
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(new DemoThreadB());
Thread t2 = new Thread(new DemoThreadB());
t1.start();
t2.start();
Thread.sleep(1000);
Log.info(t2.getState());
System.exit(0);
}
}
class DemoThreadB implements Runnable {
@Override
public void run() {
commonResource();
}
public static synchronized void commonResource() {
while(true) {
// Infinite loop to mimic heavy processing
// 't1' won't leave this method
// when 't2' try to enter this
}
}
}
4、WAITING
线程在等待其他线程执行特定操作时处于WAITING状态。 根据 JavaDocs,任何线程都可以通过调用以下三种方法中的任何一种进入此状态:
wait()、thread.join()、LockSupport.park()
参考以下代码。
(1)创建并启动了t1
(2)t1创建一个t2并启动它
(3)当t2继续处理时,我们调用t2.join(),这将t1置于WAITING状态,直到t2完成执行
(4)由于t1正在等待t2完成,我们从t2调用t1.getState(),状态输出WAITING
public class WaitingState implements Runnable {
public static Thread t1;
public static void main(String[] args) {
t1 = new Thread(new WaitingState());
t1.start();
}
public void run() {
Thread t2 = new Thread(new DemoThreadWS());
t2.start();
try {
t2.join();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
Log.error("Thread interrupted", e);
}
}
}
class DemoThreadWS implements Runnable {
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
Log.error("Thread interrupted", e);
}
Log.info(WaitingState.t1.getState());
}
}
5、TIMED_WAITING
当线程在规定的时间内等待另一个线程执行特定操作时,它处于TIMED_WAITING状态。
根据 JavaDocs,有五种方法可以将线程置于TIMED_WAITING状态:
thread.sleep(long millis)
wait(int timeout) or wait(int timeout, int nanos)
thread.join(long millis)
LockSupport.parkNanos
LockSupport.parkUntil
参考以下代码,创建并启动了一个线程t1 ,该线程进入休眠状态,超时时间为 5 秒;输出将是:TIMED_WAITING。
public class TimedWaitingState {
public static void main(String[] args) throws InterruptedException {
DemoThread obj1 = new DemoThread();
Thread t1 = new Thread(obj1);
t1.start();
// The following sleep will give enough time for ThreadScheduler
// to start processing of thread t1
Thread.sleep(1000);
Log.info(t1.getState());
}
}
class DemoThread implements Runnable {
@Override
public void run() {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
Log.error("Thread interrupted", e);
}
}
}
6、TERMINATED
这是死线程的状态。当它完成执行或异常终止时,它处于TERMINATED状态。
参考以下代码,当我们启动线程t1时,下一条语句Thread.sleep(1000)为t1提供了足够的时间来完成,所以这个程序给我们的输出如下:TERMINATED。
public class TerminatedState implements Runnable {
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(new TerminatedState());
t1.start();
// The following sleep method will give enough time for
// thread t1 to complete
Thread.sleep(1000);
Log.info(t1.getState());
}
@Override
public void run() {
// No processing in this block
}
}
除了线程状态,我们还可以通过 isAlive() 方法来判断线程是否存活。例如,如果我们在这个线程上调用isAlive() 方法,也会返回false。总之,当且仅当线程已启动且尚未死亡时,线程才是活着的。
Log.info(t1.isAlive());
二、总结
当需要使用Java线程在多线程环境下进行编程时,理解Java的线程周期与线程的状态是非常重要的。