Java 线程 3 - 线程的生命周期

参考:

Java 线程 0 - 前言

java.lang Enum Thread.State

Java疯狂讲义 - 线程的生命周期


学习 Java 线程的生命周期以及相关的函数


主要内容:

  1. 线程生命周期
  2. getStateisAlive
  3. sleepyieldjoin
  4. interruptisInterruptedinterrupted

线程生命周期

参考:java.lang Enum Thread.State

A thread state. A thread can be in one of the following states:  
    * NEW - A thread that has not yet started is in this state.  
    * RUNNABLE - A thread executing in the Java virtual machine is in this state.  
    * BLOCKED - A thread that is blocked waiting for a monitor lock is in this state.  
    * WAITING - A thread that is waiting indefinitely for another thread to perform a particular action is in this state.    
    * TIMED_WAITING - A thread that is waiting for another thread to perform an action for up to a specified waiting time is in this state.  
    * TERMINATED - A thread that has exited is in this state.  

Java 定义线程生命周期共有 6 种状态:

  • NEW
  • RUNNABLE
  • BLOCKED
  • WAITING
  • TIMED_WAITED
  • TERMINATED

各状态变化如下图所示:

这里写图片描述

NEW

使用 new 关键字创建新线程后,该线程就处于 新建 状态。此时,Java 虚拟机为其分配了内存,并初始化了成员变量

RUNNABLE

调用 start 方法后,线程就处于 可运行 状态。Java 虚拟机为其创建方法调用栈和程序计数器,但是线程是否已经运行取决于 Java 虚拟机线程调度器的调度,也就是说,当线程状态为 RUNNABLE 时,并不确定线程是否已经开始执行 run 方法

当没有足够的处理器资源时,线程调用 start 方法后并不会立即执行 run 方法。此时可以暂停当前线程 1毫秒,即可让就绪线程立即开始运行

BLOCKEDWAITINGTIMED_WAITING

内部对象锁

参考:《Java核心技术 卷I - 14.5.5 synchronized关键字》

Java 1.0 开始,每一个 Java 对象都有一个内部锁。如果方法使用 synchronized 关键字声明,即使用内部对象锁来保护方法。所以,必须先获取对象的内部锁,才能调用该方法

BLOCKED

当一个线程试图获取一个对象的内部锁,但该锁已被其它线程持有,那么,该线程就处于 阻塞 状态,直到其它线程释放该锁,并且线程调度器允许该线程持有,此时线程处于非阻塞状态

WAITINGTIMED_WAITING

当线程等待另一个线程通知调度器一个条件(condition)时,该线程处于 等待 状态

即调用 Object.wait 或者 Thread.join,或者使用 LockCondition 时,线程处于等待状态

如果线程除了等待另一个线程的通知外,还会计算等待时间,如果超过一定时间,自动回到 可运行 状态,那么此时线程进入的是 计时等待 状态

带有超时参数的方法:Thread.sleepThread.waitThread.join,以及 Lock.tryLockCondition.await

TERMINATED

线程 终止 有两种情况:

  1. 正常运行 run 方法结束
  2. run 方法中遇到一个未捕获异常而终止

《Java疯狂讲义 16.3 线程的生命周期》

《Java疯狂讲义》 中,将线程生命周期分为 5 个状态:

  • 新建(New
  • 就绪(Runnable
  • 运行(Running
  • 阻塞(Blocked
  • 死亡(Dead

其状态变化图如下图所示:

这里写图片描述

它将 Java 中的 可运行 状态分为了 就绪运行 状态,等待处理器资源的属于 就绪 状态;已获得资源开始运行 run 方法的属于 运行 状态

阻塞/等待/计时等待 合并为 阻塞 状态

里面列出了多个处于阻塞状态的情况:

  1. 线程调用 sleep 方法主动放弃所占用的处理器资源
  2. 线程调用了一个阻塞式 IO 的方法,在该方法返回之前,该线程被阻塞
  3. 线程试图获取一个同步监视器,但该同步监视器被其它线程所占有
  4. 线程在等待某个通知(notify

由上面可知,情况 1 属于 计时等待 状态,情况 2 属于 等待 状态,情况 3 属于 阻塞 状态,情况 4 属于 等待/计时等待 状态

解除上面处于 阻塞 状态的方法如下:

  1. 调用 sleep 方法的线程经过了指定时间
  2. 线程调用的阻塞式 IO 方法已经返回
  3. 线程成功地获得了试图获取的同步监视器
  4. 其它线程发出了通知

getStateisAlive

参考:

getState()

isAlive()

判断当前线程生命周期状态,可以使用方法 getState

/**
 * Returns the state of this thread.
 * This method is designed for use in monitoring of the system state,
 * not for synchronization control.
 *
 * @return this thread's state.
 * @since 1.5
 */
public State getState() {
    // get current thread state
    return sun.misc.VM.toThreadState(threadStatus);
}

判断线程是否存活,使用方法 isAlive

/**
 * Tests if this thread is alive. A thread is alive if it has
 * been started and has not yet died.
 *
 * @return  <code>true</code> if this thread is alive;
 *          <code>false</code> otherwise.
 */
public final native boolean isAlive();

sleepyieldjoin

参考:

sleep

yield

join

sleep

使用方法 sleep 可以暂停当前执行线程指定时间,调用此方法后,线程从 可运行 状态转向 计时等待 状态,其有两个重载函数:

public static void sleep(long millis) throws InterruptedException
public static void sleep(long millis, int nanos) throws InterruptedException

通常仅指定暂停毫秒数(其精确性和准确性依赖于系统定时器和调度器

/**
 * Causes the currently executing thread to sleep (temporarily cease
 * execution) for the specified number of milliseconds, subject to
 * the precision and accuracy of system timers and schedulers. The thread
 * does not lose ownership of any monitors.
 *
 * @param  millis
 *         the length of time to sleep in milliseconds
 *
 * @throws  IllegalArgumentException
 *          if the value of {@code millis} is negative
 *
 * @throws  InterruptedException
 *          if any thread has interrupted the current thread. The
 *          <i>interrupted status</i> of the current thread is
 *          cleared when this exception is thrown.
 */
public static native void sleep(long millis) throws InterruptedException;

public static void sleep(long millis, int nanos)
throws InterruptedException {
    if (millis < 0) {
        throw new IllegalArgumentException("timeout value is negative");
    }

    if (nanos < 0 || nanos > 999999) {
        throw new IllegalArgumentException(
                            "nanosecond timeout value out of range");
    }

    if (nanos >= 500000 || (nanos != 0 && millis == 0)) {
        millis++;
    }

    sleep(millis);
}

从重载方法实现中可看出,Java 系统仅将暂停时间精确到毫秒级

yield

方法 yield 用于提示系统当前线程愿意放弃处理器资源,回到就绪状态,此时如果没有优先级比当前线程高或者相等的就绪线程的话,系统会重新将处理器资源分配给当前线程

/**
 * A hint to the scheduler that the current thread is willing to yield
 * its current use of a processor. The scheduler is free to ignore this
 * hint.
 *
 * <p> Yield is a heuristic attempt to improve relative progression
 * between threads that would otherwise over-utilise a CPU. Its use
 * should be combined with detailed profiling and benchmarking to
 * ensure that it actually has the desired effect.
 *
 * <p> It is rarely appropriate to use this method. It may be useful
 * for debugging or testing purposes, where it may help to reproduce
 * bugs due to race conditions. It may also be useful when designing
 * concurrency control constructs such as the ones in the
 * {@link java.util.concurrent.locks} package.
 */
public static native void yield();

对于调用线程来说,yield 方法的使用并没有改变线程的状态(一直在 Runnable 状态)

join

join 方法有 3 种重载形式:

public final void join() throws InterruptedException
public final void join(long millis) throws InterruptedException
public final void join(long millis, int nanos) throws InterruptedException

如果线程 A 在线程 B 中调用了 join 方法,那么线程 B 需要等待线程 A 运行结束或者等待一段时间

public final void join() throws InterruptedException {
    join(0);
}

/**
 * Waits at most {@code millis} milliseconds for this thread to
 * die. A timeout of {@code 0} means to wait forever.
 *
 * <p> This implementation uses a loop of {@code this.wait} calls
 * conditioned on {@code this.isAlive}. As a thread terminates the
 * {@code this.notifyAll} method is invoked. It is recommended that
 * applications not use {@code wait}, {@code notify}, or
 * {@code notifyAll} on {@code Thread} instances.
 *
 * @param  millis
 *         the time to wait in milliseconds
 *
 * @throws  IllegalArgumentException
 *          if the value of {@code millis} is negative
 *
 * @throws  InterruptedException
 *          if any thread has interrupted the current thread. The
 *          <i>interrupted status</i> of the current thread is
 *          cleared when this exception is thrown.
 */
public final synchronized void join(long millis)
throws InterruptedException {
    long base = System.currentTimeMillis();
    long now = 0;

    if (millis < 0) {
        throw new IllegalArgumentException("timeout value is negative");
    }

    if (millis == 0) {
        while (isAlive()) {
            wait(0);
        }
    } else {
        while (isAlive()) {
            long delay = millis - now;
            if (delay <= 0) {
                break;
            }
            wait(delay);
            now = System.currentTimeMillis() - base;
        }
    }
}

public final synchronized void join(long millis, int nanos)
throws InterruptedException {

    if (millis < 0) {
        throw new IllegalArgumentException("timeout value is negative");
    }

    if (nanos < 0 || nanos > 999999) {
        throw new IllegalArgumentException(
                            "nanosecond timeout value out of range");
    }

    if (nanos >= 500000 || (nanos != 0 && millis == 0)) {
        millis++;
    }

    join(millis);
}

从重载方法中可知,最终调用的是 join(long millis)。如果没有任何参数,表示当前线程需要等待调用线程运行结束;如果有输入时间参数(毫秒单位),那么等待一段时间后即可运行

join 方法使当前线程从 运行时 状态进入 等待/计时等待 状态


interruptisInterruptedinterrupted

参考:

interrupt

isInterrupted

interrupted

线程中断相关的函数有如下 3 个:

public void interrupt()
public boolean isInterrupted()
public static boolean interrupted()

其使用场景并没有特别理解,线程对象调用 interrupt() 方法后,仅仅设置了中断位,并没有进一步的操作(进一步的操作需要自己去定义,可以忽略中断,也可以进行其它操作

而且如果遇到线程处于 阻塞/等待/计时等待 的状态下,还会抛出 InterruptedExcpetion

在知乎上找了几个资料:

线程的中断(interrupt)机制

Java里一个线程调用了Thread.interrupt()到底意味着什么?

猜你喜欢

转载自blog.csdn.net/u012005313/article/details/78410808