java 多线程系列文章列表, 请查看目录: 《java 多线程学习笔记》
1. Callable 方式
Callable 方式最主要的特点就是可以阻塞式获取子线程执行结果, 也就是说对于长耗时的任务,可以放到子线程中执行, 而主线程去执行其它任务. 当主线程执行完其它任务之后, 需要获取子线程任务返回结果时, 如果子线程没有执行完, 主线程会阻塞, 等子线程执行完之后, 再继续执行.
1.1 Callable 特点
- 面向接口编程, 松耦合设计
- 在多线程模式下,可实现对象资源共享. 但是需要注意使用原子类变量AtomicXXX
- 不能独立运行, 需绑定在Thread实例上运行
- 有返回值, 主线程可等待异步线程执行结果, 可捕获异步线程异常
1.2 使用步骤
- 自定义Callable 子类, 并创建子类实例. 可使用普通类, 匿名内部类, lambda 表达式等方式
- 创建异步任务, Future 接口实例
- 创建线程, 绑定Future任务, 并启动异步线程
- 阻塞获取异步线程结果
1.3 适用场景
- 主线程需要等待异步任务执行结束时
- 主线程获取异步任务执行结果时
- 主线程需要捕获异步线程异常时
- 主线程需要
1.4 Future API
方法签名 | 方法描述 | 参数 |
---|---|---|
boolean cancel(boolean mayInterruptIfRunning) | 取消异步任务. | 如过正在运行, 是否强制取消 |
boolean isCancelled() | 子线程是否被取消 | 无 |
boolean isDone() | 子线程是否结束, 子线程被取消也会返回true | 无 |
V get() | 获取异步线程返回结果, 如果线程被取消或执行过程抛出异常, 则会抛出异常 | 无 |
V get(long timeout, TimeUnit unit) | 获取异步线程返回结果, 指定阻塞时间, 超时抛出异常 | 阻塞等待时间 |
2. 场景示例
2.1 主线程等待异步线程结束
- 异步线程执行长耗时任务时, 主线程不阻塞, 继续执行任务
- 主线程获取异步线程执行结果时, 如果异步线程未结束或进行阻塞
- 异步线程执行过程抛出异常时, 主线程可在获取异步线程执行结果时捕获
public static void main(String[] args){
// 1. 创建Callable 实例
Callable callable =new Callable<String>(){
public String call() throws Exception {
String time = Thread.currentThread().getName() + "-" + LocalDateTime.now().toString();
Thread.sleep(5000);
System.out.println("子线程计算完毕:" + time);
return time;
}
};
// 2. 创建异步任务
FutureTask<String> future = new FutureTask<>(callable);
// 3. 创建线程并绑定异步任务
Thread thread = new Thread(future);
// 4. 启动异步线程
thread.start();
// 5. 主线程无阻塞继续执行任务
System.out.println("主线程执行其它任务...");
// 6. 阻塞获取异步任务结果
try {
String result = future.get();
System.out.println("result:" + result);
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
2.2 主线程捕获异步线程异常
主线程在获取异步线程执行结果时, 会捕获到异步线程的异常
public static void main(String[] args){
// 1. 创建Callable 实例
Callable callable =new Callable<String>(){
public String call() throws Exception {
// 抛出异常
int a = 1/0;
return null;
}
};
// 2. 创建异步任务
FutureTask<String> future = new FutureTask<>(callable);
// 3. 创建线程并绑定异步任务
Thread thread = new Thread(future);
// 4. 启动异步线程
thread.start();
// 5. 阻塞获取异步任务结果
try {
String result = future.get();
System.out.println("result:" + result);
} catch (InterruptedException | ExecutionException e) {
System.out.println("主线程捕获到子线程异常: " + e.getMessage());
}
}
2.3 主线程取消异步线程
- 当异步线程正在执行时, 主线程可主动取消异步任务.
- 取消任务时如果设置为true, 则表示强制取消, 无论异步任务是否在运行. 如果设置为false, 则表示如果异步任务已经开始运行,则不进行取消.
public static void main(String[] args) throws InterruptedException {
// 1. 创建Callable 实例
Callable callable =new Callable<String>(){
public String call() throws Exception {
String time = Thread.currentThread().getName() + "-" + LocalDateTime.now().toString();
Thread.sleep(5000);
System.out.println("子线程计算完毕:" + time);
return time;
}
};
// 2. 创建异步任务
FutureTask<String> future = new FutureTask<>(callable);
// 3. 创建线程并绑定异步任务
Thread thread = new Thread(future);
// 4. 启动异步线程
thread.start();
// 5. 主线程无阻塞继续执行任务
System.out.println("主线程执行其它任务...");
// 休眠2秒
Thread.sleep(2000l);
// 强制结束异步线程
future.cancel(true);
// 6. 阻塞获取异步任务结果
try {
String result = future.get();
System.out.println("result:" + result);
} catch (Exception e) {
System.out.println("主线程捕获到异步线程异常: " + e.getMessage());
System.out.println("异步线程: isDone:" + future.isDone() + ", isCancle:" + future.isCancelled());
e.printStackTrace();
}
}