Java面试篇基础部分 - Java线程生命周期
目录
引言
在Java中,线程是一种轻量级的进程,它可以并行地执行程序的代码。理解Java线程的生命周期对Java开发者来说至关重要,因为它直接影响到程序的性能和可靠性。在面试中,面试官常常会询问线程的基本概念和生命周期的状态变化。本文将深入探讨Java线程的生命周期,结合实例和场景来加深理解。
线程的基本概念
在Java中,线程是程序执行的基本单位。一个Java程序可以包含多个线程,线程之间可以并发执行。Java线程可以通过以下方式创建:
- 继承
Thread
类 - 实现
Runnable
接口
创建线程的基本步骤如下:
- 定义一个类,继承
Thread
类或实现Runnable
接口。 - 重写
run()
方法。 - 创建线程对象,并调用
start()
方法。
Java线程的生命周期
Java线程的生命周期可以分为以下几个状态:
新建状态
当线程对象被创建后,它就处于新建状态。此时,线程尚未启动。
javaCopy CodeThread thread = new Thread(new MyRunnable()); // 线程在此时处于新建状态
runnable 状态
当线程的start()
方法被调用后,线程进入可运行状态。此时,线程可以被CPU调度执行。
javaCopy Codethread.start(); // 线程从新建状态进入可运行状态
阻塞状态
当线程试图获取一个对象的锁,但该锁被其他线程占用时,线程进入阻塞状态。此时,线程无法继续执行,直到获得锁。
javaCopy Codesynchronized (lock) {
// 代码块
} // 如果其他线程持有该锁,当前线程将进入阻塞状态
等待状态
当线程调用wait()
方法时,它会进入等待状态,直到另一个线程调用notify()
或notifyAll()
方法。处于等待状态的线程不会占用CPU资源。
javaCopy Codesynchronized (lock) {
lock.wait(); // 线程进入等待状态
}
超时等待状态
当线程调用wait(long timeout)
或join(long timeout)
方法时,它会进入超时等待状态,直到超时或被其他线程唤醒。
javaCopy Codesynchronized (lock) {
lock.wait(1000); // 线程进入超时等待状态,最多等待1秒
}
终止状态
当线程的run()
方法执行完毕,或由于异常而结束时,线程进入终止状态。此时,线程无法被再次启动。
javaCopy Codepublic void run() {
// 执行完毕,线程进入终止状态
}
线程生命周期的状态转移
线程状态之间的转移关系如下:
- 新建状态 -> 可运行状态:调用
start()
方法。 - 可运行状态 -> 阻塞状态:尝试获取锁失败。
- 可运行状态 -> 等待状态:调用
wait()
方法。 - 可运行状态 -> 超时等待状态:调用
wait(long timeout)
或join(long timeout)
方法。 - 可运行状态 -> 终止状态:
run()
方法执行完毕或抛出异常。 - 等待状态 -> 可运行状态:被其他线程调用
notify()
或notifyAll()
方法。 - 超时等待状态 -> 可运行状态:超时或被唤醒。
案例分析
在这一部分,我们将通过实际代码示例来进一步理解线程的生命周期。
示例一:新建状态到可运行状态
javaCopy Codeclass 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 Codeclass 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 Codeclass 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 Codeclass 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 Codeclass 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线程的生命周期有了更深入的理解,能够在