java 多线程面试题
时间: 2025-01-02 08:33:23 浏览: 7
### Java多线程常见面试问题及答案
#### 1. `submit()` 和 `execute()` 方法有何不同?
`ThreadPoolExecutor` 提供了两种提交任务的方法:`submit()` 和 `execute()`。主要区别在于返回值和处理方式上:
- `execute(Runnable command)` 只接受 `Runnable` 类型的任务,并且不会返回任何结果。
- `submit(Callable<T> task)` 或者 `submit(Runnable task, T result)` 不仅可以接收 `Callable` 类型的任务还可以指定默认的结果对象,它能够返回一个表示异步计算的 `Future<V>` 对象。
当尝试向已关闭或无法再接受新任务的线程池提交任务时,这两种方法都会抛出 RejectedExecutionException 异常[^2]。
```java
// 使用 execute()
executor.execute(new Task());
// 使用 submit(), 返回 Future 对象以便获取执行结果
Future<?> future = executor.submit(new CallableTask());
try {
Object result = future.get();
} catch (InterruptedException | ExecutionException e) {
Thread.currentThread().interrupt(); // Restore interrupted status
}
```
#### 2. 创建线程的方式有哪些优缺点?
创建线程主要有两种常用模式——通过继承 `Thread` 类重写其 run 方法来定义行为;另一种则是实现 `Runnable` 接口并将其传递给 `Thread` 构造函数或其他容器组件如 ExecutorService 来管理生命周期。
##### 继承 Thread 方式的特点:
- **优点**: 实现简单直观;
- **缺点**: 占用了类层次结构中的唯一继承机会,由于 Java 支持单一继承模型因此限制较大[^4]。
##### 实现 Runnable 接口的优点与不足之处分别为:
- **优点**: 避免了单继承特性的约束,允许多个线程共享相同的逻辑单元即实现了更好的代码复用性和灵活性;
- **缺点**: 设计相对复杂一点,在某些场景下可能不如直接扩展自 Thread 明确易懂。
#### 3. 同一 Java 线程能否被启动多次?
不可以重复启动已经结束过的线程实例。一旦某个线程完成了它的运行周期(无论是正常完成还是因未捕获异常而提前终止),再次对该线程调用 start() 将引发 IllegalThreadStateException 运行期错误提示。这表明试图非法改变线程状态的行为违反了 JVM 定义的状态转换规则[^3]。
```java
thread.start(); // 正确做法
// thread.start(); // 错误示范,将导致 IllegalStateException 被抛出
```
#### 4. 睡眠(`sleep`)同等待(`wait`)机制间存在哪些差异?
两者都是用来暂停当前正在执行的线程一段时间的技术手段,但它们作用的对象以及应用场景有所不同:
- `Thread.sleep(long millis)` 是静态成员函数属于 java.lang.Thread 类的一部分,适用于让当前线程休眠指定毫秒数而不释放锁资源;
- `Object.wait()` 则是非静态成员关联于特定对象监视器上的操作,只有处于 synchronized 块内的线程才允许调用此法,并且会放弃持有锁直至收到通知唤醒为止。
```java
synchronized(lock){
try{
lock.wait(timeout); // 必须放在同步上下文中使用
}catch(InterruptedException ie){}
}
Thread.sleep(durationInMillis); // 直接调用无需额外条件
```
#### 5. 线程 vs 进程的主要特征对比分析
| 特征 | 线程 | 进程 |
|-------------|--------------------------------------------|
| 描述 | CPU调度的基本单位 | 操作系统独立执行的一个程序 |
| 关系 | 至少有一个 | 包含一个或多个 |
| 开销大小 | 较低 | 更高,因为它涉及到更多初始化工作 |
| 是否拥有自己内存空间 | 共享所属进程地址空间 | 自己私有的虚拟地址空间 |
| 影响范围 | 如果发生致命错误可能会牵连到整个进程 | 失败通常只会影响自身 |
此外值得注意的是,尽管线程之间相互协作紧密但也正因为如此容易造成数据竞争等问题所以需要引入诸如 volatile、synchronized 等关键字来进行必要的保护措施[^5].
阅读全文