Java多线程面试重点:创建线程方式与线程状态

需积分: 5 0 下载量 124 浏览量 更新于2024-08-04 收藏 8KB MD 举报
本文主要探讨了Java中多线程的相关面试题目,包括创建线程的三种方式、线程的生命周期及状态转换、线程启动的限制、wait与sleep方法的区别,以及wait方法的中断特性。 ### 创建线程的三种方式 1. **继承Thread类**: 当自定义类继承Thread类时,可以通过重写`run()`方法来实现线程的逻辑。然后通过创建该类的实例并调用`start()`方法启动线程。这种方式中,每个线程有自己的副本(即类的实例),因此可能会占用更多内存。 2. **实现Runnable接口**: 实现Runnable接口,同样需要重写`run()`方法。然后将Runnable对象作为参数传递给Thread类的构造函数,创建Thread对象并调用其`start()`方法启动线程。这种方式更加灵活,因为Java不支持多重继承,但可以与其他接口一起使用。 3. **实现Callable接口**: Callable接口提供了`call()`方法,它允许线程返回一个结果。与Runnable不同,Callable任务可以抛出一个受检查的或未经检查的异常。要使用Callable,需要创建一个FutureTask,将Callable对象作为参数传入,然后使用FutureTask来启动线程。调用`get()`方法可以获取结果,该方法会阻塞直到结果可用。 ### 线程的生命周期与状态转换 线程的生命周期包括新建、就绪、运行、阻塞和死亡五种状态。线程状态的转换通常由以下API控制: - `start()`: 将新建状态的线程变为就绪状态,准备进入CPU执行。 - `sleep(long time)`: 使当前运行的线程进入阻塞状态,等待指定时间后再变为就绪状态。 - `join()`: 让当前线程等待指定线程结束后再继续执行。 - `yield()`: 让当前线程从运行状态变为就绪状态,让其他线程有机会执行。 - `interrupt()`: 中断线程,如果线程在阻塞状态(如wait、sleep或join),则会抛出InterruptedException。 ### 重复调用start方法 如果对同一个线程对象调用两次`start()`方法,第二次调用会抛出`IllegalThreadStateException`,因为线程只允许启动一次。 ### wait、notify和notifyAll方法 - **wait()**: 此方法会使当前持有锁的线程进入等待状态,释放锁以便其他线程可以获取。线程需要被其他线程通过`notify()`或`notifyAll()`唤醒,或者等待时间到达后自动唤醒。唤醒后并不立即恢复执行,而是需要重新竞争锁。 - **notify()** 和 **notifyAll()**: 这两个方法用于唤醒等待在特定锁上的线程。`notify()`只会唤醒一个线程,而`notifyAll()`会唤醒所有等待的线程。唤醒后,线程也需要获取锁才能继续执行。 - **wait()、notify()和notifyAll()** 是Object类的`final native`方法,无法被重写,并且必须在同步代码块或同步方法中调用,否则会抛出`IllegalMonitorStateException`。 ### sleep()方法 与wait()不同,`sleep()`方法会让当前线程休眠指定时间,但不会释放锁。当睡眠时间结束,线程会自动恢复执行,无需其他线程唤醒。如果在持有锁的情况下调用`sleep()`,线程会在睡眠结束后继续持有锁。 多线程编程涉及到线程的创建、同步、通信和管理,理解这些概念对于编写高效的并发程序至关重要。在实际开发中,合理使用线程池、同步机制和并发工具类可以更好地控制线程行为,提高系统性能。