2024-03-23
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 本文链接:https://www.skjava.com/mianshi/baodian/detail/1839336608

Java 中的线程有如下六种状态:

  1. 新建(New): 当线程对象被创建后,它就处于新建状态。在这个状态下,线程还未开始执行。
  2. 可运行(Runnable): 调用线程的 start() 后,线程就进入可运行状态。
    1. 在这个状态下,线程可能正在运行,这时线程是运行中(Running)
    2. 也可能正在等待系统为其分配处理器资源,这时线程是就绪(Ready)
  3. 阻塞(Blocked): 当线程试图获取一个锁(synchronized资源)而该锁被其他线程持有时,则该线程进入阻塞状态。线程会在这个状态下等待,直到它可以获取到锁。
  4. 等待(Waiting): 当线程等待其他线程完成特定操作时,它会进入等待状态。例如,当线程调用 Object.wait()Thread.join()LockSupport.park() 时,它会进入这个状态。
  5. 超时等待(Timed Waiting): 这个状态与等待状态类似,但有时间限制。例如,当线程调用 Thread.sleep(long millis)Object.wait(long timeout)Thread.join(long millis)LockSupport.parkNanos()/parkUntil() 时,它会进入定时等待状态。
  6. 终止(Terminated): 当线程的 run() 方法执行完成后,它会进入终止状态。这意味着线程的任务已经结束了。

状态转换

几个方法的比较

  • Thread.sleep(long millis):当前线程调用该方法后,线程进入超时等TIMED_WAITING)状态,但不释放对象锁,millis后线程自动苏醒进入就绪状态Ready)。注意,该方法不会释放任何监视器(锁),如果一个线程持有锁,当它调用 sleep() 方法时,它仍然会保持对这些锁的持有。这意味着其他需要这些锁的线程可能会被阻塞,直到该线程醒来并释放锁。
  • Thread.yield():当前线程调用该方法后,线程会放弃获取的CPU时间片,但不释放锁资源,线程由运行状态Running)变为就绪状态Ready),让操作系统再次选择线程。它的作用是使当前正在执行的线程暂停执行,给其他同等优先级的线程执行的机会但并不保证一定会轮流执行,因为让步的线程还有可能被线程调度程序再次选中。Thread.yield()不会导致阻塞。该方法与sleep() 类似,只是不能由用户指定暂停多长时间
  • Thread.join()/Thread.join(long millis):当前线程里调用其它线程 Tjoin(),当前线程进入WAITING/TIMED_WAITING状态,当前线程不会释放已经持有的对象锁。线程T执行完毕或者millis时间到,当前线程一般情况下进入RUNNABLE状态,也有可能进入BLOCKED状态(因为join是基于wait实现的)。
  • Object.wait():当前线程调用对象的wait()方法,当前线程释放对象锁,进入等待队列。依靠notify() / notifyAll()唤醒或者wait(long timeout) timeout时间到自动唤醒。需要注意的是Object.wait() 必须要在同步代码块或同步方法中调用,这意味着调用它的线程必须持有该对象的监视器锁。当一个线程调用 wait() 时,它会释放持有的监视器锁并进入等待状态,则其他线程就可以获取锁并执行同步的代码块或方法。
  • Object.notify():当一个线程调用 notify() 时,它会从这个对象的等待池中随机唤醒一个正在执行 wait() 方法的线程。被唤醒的线程会尝试重新获得监视器锁。一旦获得锁,它就可以继续执行。notifyAll() 唤醒是在此对象监视器上等待的所有线程。与 wait() 方法一样,notify() 也必须在同步代码块或同步方法中调用
  • LockSupport.park()/LockSupport.parkNanos(long nanos),LockSupport.parkUntil(long deadlines):该类方法用于阻塞当前线程,直到获得许可(permit)或者线程被中断。调用这类方法的线程进入WAITING/TIMED_WAITING状态。可以通过 LockSupport.unpark(Thread thread) 方法被唤醒。
    • LockSupport 使用一种许可(permit)机制。每个线程都有一个与之关联的许可,许可不能累积,它的数量只能是 0 或 1。
    • 如果一个线程已经拥有许可,那么park() 方法将立即返回,然后消耗这个许可。如果没有许可,park() 会阻塞线程。
    • 当调用 unpark() 时,会给指定线程发放一个许可(如果它之前没有许可的话),从而使得被 park() 阻塞的线程返回。
阅读全文