前面我们介绍了创建线程的三种基本方法,学会如何创建和运行线程之后,接下来就是对线程生命周期的了解,本篇我们介绍Java线程的声明周期,并且使用一些案例演示声明周期的变化。在Thread类中,使用枚举类为Java线程定义了六种状态,代码如下:
public enum State {
NEW,
RUNNABLE,
BLOCKED,
WAITING,
TIMED_WAITING,
TERMINATED;
}
在上述状态中,NEW:初始状态,线程被构建,但是还没有调用 start 方法;RUNNABLED:运行状态,称为运行状态,JAVA 线程把操作系统中的就绪和运行两种状态统一称为“运行状态”,因为CPU是采取时间片的方式执行线程,当CPU将时间片分配制其他线程时,该线程就处于就绪状态,当该线程获取到时间片时,该线程处于运行状态。WAITING和TIMED_WAITING是等待状态,他们的区别是TIME_WAITING是超时等待状态,超时以后自动返回。TERMINATED表示终止状态,表示当前线程执行完毕,BLOCKED表示阻塞状态。我们可以使用图示表示线程状态之间的转换情况如下图,记住了下面的图例基本上就掌握了线程的声明周期。
下面我们针对上图对Java线程的生命周期进行演示,首先是NEW状态,在这之前我们先使用继承的方式创建两个线程,并且创建一个测试类,这是不再展示代码,可以参考线程的创建一篇博文。我们创建一个线程实例,然后输出其状态可以看到结果我NEW,代码如下:
static void NewStateTest() {
ThreadA ta = new ThreadA();
//输出为NEW
System.out.println(ta.getState());
}
还是使用ThreadA实例,实例创建之后调用start()方法,启动线程,然后输出线程的状态,可以看到输出结束为RUNNABLE,代码如下所示:
static void runnableStateTest() {
ThreadA ta = new ThreadA();
ta.start();
//输出为RUNNABLE
System.out.println(ta.getState());
}
TERMINATED表示终止状态,表示当前线程执行完毕,我们调用start()方法,为了能够确保线程执行完毕,我们在主线程sleep一秒,代码如果所示,结果会输出TERMINATED
static void terminalStateTest() throws InterruptedException {
ThreadA ta = new ThreadA();
ta.start();
Thread.sleep(1000);
//输出为TERMINATED
System.out.println(ta.getState());
}
WAITING和TIMED_WAITING都表示线程处于等待状态,区别是WAITING状态的线程在没有被唤醒之前一直处于等待中,而TIMED_WAITING则有一个等待超时的时间。线程处于WAITING状态是因为调用了Object.wait()、Thread.join()、LockSupport.park()方法,相对应的需要调用Object.notify()、Object.notifyAll()或者LockSupport.unPark()去唤醒等待中的线程。比如一个线程调用了Object.wait()方法将会一直等待,直到另一个线程调用Object.notify()或者Object.notifyAll()方法。一个调用Thread.join()的线程将会等待指定的线程终止才会被唤醒。TIMED_WAITING的用法与WAITING类似,他提供了Object.wait(long time)、Thread.sleep(long time)、Thread.join(long time)、LockSupport.partNanos和parkUtil方法。如下是我们使用wait和notify的一个案例:
public class ThreadB extends Thread{
public Object lock = null;
public ThreadB(Object lock) {
this.lock = lock;
}
@Override
public void run() {
synchronized (lock) {
try {
System.out.println("lock.wait()方法调用 ");
//调用Object.wait方法,线程处于WAITING状态
lock.wait();
System.out.println("Thread B 运行 ");
} catch (InterruptedException e) {
}
}
}
}
static void waitingStateTest() throws InterruptedException {
Object lock = new Object();
ThreadB tb = new ThreadB(lock);
tb.start();
Thread.sleep(1000);
//输出为WAITING
System.out.println(tb.getState());
Thread.sleep(2000);
synchronized (lock) {
//调用Object.notify()方法通知线程继续运行
lock.notify();
}
}
上面的示例中需要注意的是Object.wait和Object.notify()都是在synchronized方法块中,这设计到线程的底层原理,这里不做多的介绍,需要记住的是如果不使用synchronized方法块中会抛出 java.lang.IllegalMonitorStateException异常。其他另外的方法这里不做示例,可以自行实验。
BLOCKED表示堵塞状态,处于阻塞状态的线程正在等待监控锁进入同步块/方法或调用Object\wait()后重新进入同步块/方法会变成BLOCKED状态。我们使用下面的示例演示BLOCK状态。我们再创建线程C。同时将THREADB中的wait改为sleep(5000)
public class ThreadC extends Thread{
public Object lock = null;
public ThreadC(Object lock) {
this.lock = lock;
}
@Override
public void run() {
synchronized (lock) {
try {
System.out.println("Thread C 运行 ");
} catch (Exception e) {
}
}
}
}
我们创建测试类如下,可以输出结果C线程的状态为BLOCKED。
static void blockedStateTest() throws InterruptedException {
Object lock = new Object();
ThreadB tb = new ThreadB(lock);
ThreadC tc = new ThreadC(lock);
tb.start();
Thread.sleep(1000);
tc.start();
Thread.sleep(1000);
//输出为BLOCKED
System.out.println(tc.getState());
}
学习线程的状态和生命周期有助于对整个线程的运行过程的把握,也有助于后面更加深入线程的学习,比如synchorized等锁的学习。在学习完锁之后,我们或许就会理解为和Object.wait()方法要放在synchronized方法块中使用。