Java面试篇基础部分 - Java线程生命周期

目录

  1. 引言
  2. 线程的基本概念
  3. Java线程的生命周期
  4. 线程生命周期的状态转移
  5. 案例分析
  6. 总结

引言

在Java中,线程是一种轻量级的进程,它可以并行地执行程序的代码。理解Java线程的生命周期对Java开发者来说至关重要,因为它直接影响到程序的性能和可靠性。在面试中,面试官常常会询问线程的基本概念和生命周期的状态变化。本文将深入探讨Java线程的生命周期,结合实例和场景来加深理解。

线程的基本概念

在Java中,线程是程序执行的基本单位。一个Java程序可以包含多个线程,线程之间可以并发执行。Java线程可以通过以下方式创建:

  • 继承Thread
  • 实现Runnable接口

创建线程的基本步骤如下:

  1. 定义一个类,继承Thread类或实现Runnable接口。
  2. 重写run()方法。
  3. 创建线程对象,并调用start()方法。

Java线程的生命周期

Java线程的生命周期可以分为以下几个状态:

新建状态

当线程对象被创建后,它就处于新建状态。此时,线程尚未启动。

javaCopy Code
Thread thread = new Thread(new MyRunnable()); // 线程在此时处于新建状态

runnable 状态

当线程的start()方法被调用后,线程进入可运行状态。此时,线程可以被CPU调度执行。

javaCopy Code
thread.start(); // 线程从新建状态进入可运行状态

阻塞状态

当线程试图获取一个对象的锁,但该锁被其他线程占用时,线程进入阻塞状态。此时,线程无法继续执行,直到获得锁。

javaCopy Code
synchronized (lock) { // 代码块 } // 如果其他线程持有该锁,当前线程将进入阻塞状态

等待状态

当线程调用wait()方法时,它会进入等待状态,直到另一个线程调用notify()notifyAll()方法。处于等待状态的线程不会占用CPU资源。

javaCopy Code
synchronized (lock) { lock.wait(); // 线程进入等待状态 }

超时等待状态

当线程调用wait(long timeout)join(long timeout)方法时,它会进入超时等待状态,直到超时或被其他线程唤醒。

javaCopy Code
synchronized (lock) { lock.wait(1000); // 线程进入超时等待状态,最多等待1秒 }

终止状态

当线程的run()方法执行完毕,或由于异常而结束时,线程进入终止状态。此时,线程无法被再次启动。

javaCopy Code
public void run() { // 执行完毕,线程进入终止状态 }

线程生命周期的状态转移

线程状态之间的转移关系如下:

  • 新建状态 -> 可运行状态:调用start()方法。
  • 可运行状态 -> 阻塞状态:尝试获取锁失败。
  • 可运行状态 -> 等待状态:调用wait()方法。
  • 可运行状态 -> 超时等待状态:调用wait(long timeout)join(long timeout)方法。
  • 可运行状态 -> 终止状态:run()方法执行完毕或抛出异常。
  • 等待状态 -> 可运行状态:被其他线程调用notify()notifyAll()方法。
  • 超时等待状态 -> 可运行状态:超时或被唤醒。

案例分析

在这一部分,我们将通过实际代码示例来进一步理解线程的生命周期。

示例一:新建状态到可运行状态

javaCopy Code
class MyRunnable implements Runnable { @Override public void run() { System.out.println("线程正在运行..."); } } public class ThreadLifecycleDemo { public static void main(String[] args) { Thread thread = new Thread(new MyRunnable()); // 新建状态 thread.start(); // 进入可运行状态 } }

场景说明:在上述代码中,我们创建了一个线程并启动它。当调用start()方法时,线程就进入可运行状态。

示例二:可运行状态到阻塞状态

javaCopy Code
class SharedResource { public synchronized void access() { System.out.println(Thread.currentThread().getName() + " 正在访问资源..."); try { Thread.sleep(2000); // 模拟资源访问 } catch (InterruptedException e) { e.printStackTrace(); } } } public class BlockingExample { public static void main(String[] args) { SharedResource resource = new SharedResource(); Thread thread1 = new Thread(() -> resource.access()); Thread thread2 = new Thread(() -> resource.access()); thread1.start(); thread2.start(); // thread2将会进入阻塞状态 } }

场景说明:在这个例子中,两个线程尝试同时访问一个共享资源。由于synchronized关键字,第二个线程将进入阻塞状态,直到第一个线程释放锁。

示例三:等待状态的使用

javaCopy Code
class WaitNotifyExample { private static final Object lock = new Object(); public static void main(String[] args) { Thread waitingThread = new Thread(() -> { synchronized (lock) { try { System.out.println(Thread.currentThread().getName() + " 进入等待状态..."); lock.wait(); // 进入等待状态 } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + " 被唤醒!"); } }); Thread notifyingThread = new Thread(() -> { synchronized (lock) { System.out.println(Thread.currentThread().getName() + " 准备唤醒等待线程..."); lock.notify(); // 唤醒等待线程 } }); waitingThread.start(); notifyingThread.start(); } }

场景说明:此示例展示了一个线程进入等待状态并在另一个线程的notify()调用后被唤醒的过程。

示例四:超时等待状态的应用

javaCopy Code
class TimeoutWaitExample { private static final Object lock = new Object(); public static void main(String[] args) { Thread waitingThread = new Thread(() -> { synchronized (lock) { try { System.out.println(Thread.currentThread().getName() + " 进入超时等待状态..."); lock.wait(3000); // 进入超时等待状态 System.out.println(Thread.currentThread().getName() + " 超时或被唤醒!"); } catch (InterruptedException e) { e.printStackTrace(); } } }); waitingThread.start(); } }

场景说明:这个示例展示了一个线程在指定时间内等待,并在超时后继续执行。

示例五:线程的终止状态

javaCopy Code
class TerminationExample { public static void main(String[] args) { Thread thread = new Thread(() -> { System.out.println("线程执行中..."); // 模拟线程执行 }); thread.start(); // 启动线程 try { thread.join(); // 等待线程结束 } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("线程已经终止!"); } }

场景说明:在这个例子中,我们启动一个线程并在主线程中调用join()方法,等待其完成。当线程的run()方法执行完毕后,线程进入终止状态。

总结

Java线程的生命周期是多线程编程的重要组成部分。了解各个状态的特点及其转移关系,能够帮助开发者更有效地管理线程,提高程序的并发性能。在面试中,掌握这些概念和实例是非常必要的,以便在面对相关问题时能够自信作答。

通过以上的案例分析,我们对Java线程的生命周期有了更深入的理解,能够在