一、创建线程的多种方式
四种
1、JDK1.5 之前,传统的方式有两种
继承 Thread 类
实现 Runnable 接口
2、JDK1.5 之后,新增了两种
使用 Callable 接口
通过线程池获取线程
二、Callable 接口
目前我们学习了有两种创建线程的方法-一种是通过创建 Thread 类,另一种是通过使用 Runnable 创建线程。但是, Runnable 缺少的一项功能是,当线程终止时(即 run()完成时),我们无法使线程返回结果。为了支持此功能,
Java 中提供了 Callable 接口。

三、Callable 接口与 Runnable 接口的区别
四、Callable 如何使用
1、Thread 类的构造器
2、认识不同的人找中间人


3、FutureTask 的方法

五、Future 接口
当 call() 方法完成时,结果必须存储在主线程已知的对象中,以便主线程可以知道该线程返回的结果。为此,可以使用 Future 对象。
将 Future 视为保存结果的对象—它可能暂时不保存结果,但将来会保存(一旦Callable 返回)。 Future 基本上是主线程可以跟踪进度以及其他线程的结果的一种方式。要实现此接口,必须重写 5 种方法,这里列出了重要的方法,如下:
1 2 3 4 5 6 7 | public boolean cancel( boolean mayInterrupt): 用于停止任务。 如果尚未启动,它将停止任务。如果已启动,则仅在 mayInterrupt 为 true 时才会中断任务。 public Object get()抛出 InterruptedException, ExecutionException:用于获取任务的结果。 如果任务完成,它将立即返回结果,否则将等待任务完成,然后返回结果。 public boolean isDone(): 如果任务完成,则返回 true ,否则返回 false |
可以看到 Callable 和 Future 做两件事-Callable 与 Runnable 类似,因为它封装了要在另一个线程上运行的任务,而 Future 用于存储从另一个线程获得的结果。实际上, future 也可以与 Runnable 一起使用。
要创建线程,需要 Runnable。为了获得结果,需要 future。
六、FutureTask 概述和原理
1、FutureTask 是什么
Java 库具有具体的 FutureTask 类型,该类型实现 Runnable 和 Future,并方便地将两种功能组合在一起。 可以通过为其构造函数提供 Callable 来创建FutureTask。然后,将 FutureTask 对象提供给 Thread 的构造函数以创建Thread 对象。因此,间接地使用 Callable 创建线程。
2、核心原理
在主线程中需要执行比较耗时的操作时,但又不想阻塞主线程时,可以把这些作业交给 Future 对象在后台完成。
(1)当主线程将来需要时,就可以通过 Future 对象获得后台作业的计算结果或者执行状态;
(2)一般 FutureTask 多用于耗时的计算,主线程可以在完成自己的任务后,再去获取结果;
(3)仅在计算完成时才能检索结果;如果计算尚未完成,则阻塞 get 方法;
(4)一旦计算完成,就不能再重新开始或取消计算;
(5)get 方法而获取结果只有在计算完成时获取,否则会一直阻塞直到任务转入完成状态,然后会返回结果或者抛出异常;
(6)get 只计算一次,因此 get 方法放到最后
3、FutureTask 举例
(1)老师上课,口渴了,去买票不合适,讲课线程继续。单开启线程找班上班长帮我买水,把水买回来,需要时候直接get。
(2)4个同学, 1同学 1+2...5 , 2同学 10+11+12....50, 3同学 60+61+62, 4同学 100+200,第2个同学计算量比较大,FutureTask单开启线程给2同学计算,先汇总 1 3 4 ,最后等2同学计算位完成,统一汇总。
(3)考试,做会做的题目,最后看不会做的题目;
4、代码示例
(1)案例一
//实现Runnable接口
class MyThread1 implements Runnable {
@Override
public void run() {
}
}
//实现Callable接口
class MyThread2 implements Callable {
@Override
public Integer call() throws Exception {
System.out.println(Thread.currentThread().getName()+" come in callable");
return 200;
}
}
public class Demo1 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
//Runnable接口创建线程
new Thread(new MyThread1(),"AA").start();
//Callable接口,报错
// new Thread(new MyThread2(),"BB").start();
//FutureTask
FutureTask<Integer> futureTask1 = new FutureTask<>(new MyThread2());
//lambda 表达式
FutureTask<Integer> futureTask2 = new FutureTask<>(()->{
System.out.println(Thread.currentThread().getName() + " come in callable");
return 1024;
});
//创建线程
new Thread(futureTask2, "AA").start();//调用
System.out.println(futureTask2.get());
System.out.println(Thread.currentThread().getName() +" come over!");
}
}
(2)案例二
public class Demo2 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
//lambda 表达式
FutureTask<Integer> futureTask = new FutureTask<>(()->{
System.out.println(Thread.currentThread().getName() + " come in callable");
return 1024;
});
//创建线程
new Thread(futureTask, "AA").start();
while (!futureTask.isDone()) {
System.out.println("wait...");
}
//调用
System.out.println(futureTask.get());
//只需要计算一次,第二次直接获取
System.out.println(futureTask.get());
System.out.println(Thread.currentThread().getName() +" come over!");
}
}
整个Future只会执行一次,第二次直接获取运行结果。
(3)案例三
public class Demo1 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
//lambda 表达式
FutureTask<Integer> futureTask2 = new FutureTask<>(()->{
System.out.println(Thread.currentThread().getName() + " come in callable");
return 1024;
});
FutureTask<Integer> futureTask3 = new FutureTask<>(()->{
System.out.println(Thread.currentThread().getName() + " come in callable");
return 2048;
});
//创建线程
new Thread(futureTask2, "AA").start();
new Thread(futureTask3, "BB").start();
//调用
System.out.println(futureTask2.get());
System.out.println(futureTask3.get());
System.out.println(Thread.currentThread().getName() +" come over!");
}
}
如果多个FutureTask线程,最后汇总完毕后主线程结束。
七、总结
在主线程中需要执行比较耗时的操作时,但又不想阻塞主线程时,可以把这些作业交给 Future 对象在后台完成, 当主线程将来需要时,就可以通过 Future 对象获得后台作业的计算结果或者执行状态;
一般 FutureTask 多用于耗时的计算,主线程可以在完成自己的任务后,再去获取结果;
仅在计算完成时才能检索结果;如果计算尚未完成,则阻塞 get 方法。一旦计算完成,就不能再重新开始或取消计算。 get 方法而获取结果只有在计算完成时获取,否则会一直阻塞直到任务转入完成状态,然后会返回结果或者抛出异常。
只需要计算完成一次,下次直接获取结果,不需要再计算。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· C# 集成 DeepSeek 模型实现 AI 私有化(本地部署与 API 调用教程)
· DeepSeek R1 简明指南:架构、训练、本地部署及硬件要求
· 没有源码,如何修改代码逻辑?
· NetPad:一个.NET开源、跨平台的C#编辑器