3. 实现 Callable
接口(了解即可)
Callable
接口与 Runnable
接口类似,都可以用于定义多线程任务,但 Callable
接口的 call()
方法可以有返回值,并且可以抛出异常。下面将详细介绍如何使用 Callable
接口实现 Java 多线程。
a. Callable
接口简介
Callable
接口是 Java 并发包(java.util.concurrent
)中的一个泛型接口,其定义如下:
public interface Callable<V> {
V call() throws Exception;
}
其中,V
是返回值的类型,call()
方法是需要实现的任务逻辑,该方法可以抛出异常。
b. 使用步骤
要使用 Callable
接口实现多线程,通常需要以下几个步骤:
-
定义一个实现
Callable
接口的类:重写call()
方法,该方法包含具体的任务逻辑,并返回一个结果。 -
创建
Callable
任务实例:实例化实现了Callable
接口的类。 -
创建
ExecutorService
线程池:使用Executors
工具类创建一个线程池。ExecutorService executorService = Executors.newFixedThreadPool(2),这里表示线程池的大小为2,意味着线程池可以同时并发执行 2 个任务,如果任务数大于线程池大小,假如有5个任务,那么线程池会先让 2 个任务开始执行,剩下的 3 个任务会在任务队列中等待。当正在执行的任务完成后,线程池会从任务队列中取出新的任务继续执行,直到所有任务都执行完毕。
-
提交
Callable
任务:将Callable
任务提交给线程池,线程池会返回一个Future
对象。
Future future1 = executorService.submit(task1):
Future
是一个泛型接口,用于表示异步计算的结果。Future<Integer>
表示这个异步计算的结果是一个
Integer
类型的值。通过Future
对象,你可以在需要的时候获取任务的执行结果,例如:
java Integer result = future1.get();
get()
方法是一个阻塞方法,如果任务还未完成,调用get()
方法的线程会被阻塞,直到任务完成并返回结果。这其实是因为重写的call()方法有返回值且为Integer。
-
获取任务结果:通过
Future
对象的get()
方法获取任务的返回结果。其实就是获得call()方法的返回值。 -
关闭线程池:任务执行完毕后,关闭线程池。
c. 示例代码
import java.util.concurrent.*;
// 1. 定义一个实现 Callable 接口的类
class MyCallable implements Callable<Integer> {
private int number;
public MyCallable(int number) {
this.number = number;
}
@Override
public Integer call() throws Exception {
int sum = 0;
for (int i = 1; i <= number; i++) {
sum += i;
}
return sum;
}
}
public class CallableExample {
public static void main(String[] args) {
// 2. 创建 Callable 任务实例
MyCallable task1 = new MyCallable(10);
MyCallable task2 = new MyCallable(20);
// 3. 创建 ExecutorService 线程池
ExecutorService executorService = Executors.newFixedThreadPool(2);
try {
// 4. 提交 Callable 任务
Future<Integer> future1 = executorService.submit(task1);
Future<Integer> future2 = executorService.submit(task2);
// 5. 获取任务结果
System.out.println("Task 1 result: " + future1.get());
System.out.println("Task 2 result: " + future2.get());
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
} finally {
// 6. 关闭线程池
executorService.shutdown();
}
}
}
d. 代码解释
MyCallable
类:实现了Callable<Integer>
接口,call()
方法计算从 1 到number
的整数之和,并返回结果。ExecutorService
线程池:使用Executors.newFixedThreadPool(2)
创建一个固定大小为 2 的线程池。Future
对象:executorService.submit(task)
方法将Callable
任务提交给线程池,并返回一个Future
对象,用于表示异步计算的结果。Future.get()
方法:该方法用于获取任务的返回结果,如果任务尚未完成,该方法会阻塞当前线程,直到任务完成。executorService.shutdown()
:关闭线程池,不再接受新的任务,但会等待已提交的任务执行完毕。
e. 注意事项
- 异常处理:
call()
方法可以抛出异常,在获取任务结果时,需要处理InterruptedException
和ExecutionException
异常。 - 阻塞问题:
Future.get()
方法是一个阻塞方法,如果任务执行时间较长,会导致当前线程阻塞,直到任务完成。可以使用Future.isDone()
方法检查任务是否完成,避免阻塞。 - 线程池管理:使用完线程池后,需要调用
shutdown()
方法关闭线程池,避免资源泄漏。 - 跟实现Runnable接口不同的是:1.重写的是call方法。2.不再直接运行run方法,而是将call方法内的内容当做任务提交。3.提交后会返回一个Future泛型,其中的类型就是call方法返回的类型。4.可以使用Future类的get方法拿到返回值。4.最后记得关闭服务executorService.shutdown()。
Callable
接口与 Runnable
接口类似,但它可以返回执行结果,并且可以抛出异常。通常结合 FutureTask
类和 ExecutorService
来使用。