文章目录
6.1 线程创建的方式
- 继承Thread类
- 实现Runnable接口
- 实现Callable接口
- 线程池
6.2 Runnable接口与Callable接口对比
6.2.1 具体对比
Runnable | Callable |
---|---|
实现run()方法 | 实现call()方法 |
run()方法无返回值 | call()方法有返回值 |
不抛出异常 | 抛出异常 |
6.2.2 代码对比
// 实现Runnable接口
class MyThread1 implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"===" + "run...");
}
}
// 实现Callable接口
class MyThread2 implements Callable<Object> {
@Override
public Object call() throws Exception {
System.out.println(Thread.currentThread().getName()+"===" + "call...");
return 200;
}
}
public class CreateThreadDemo {
public static void main(String[] args) {
// 使用实现Runnable接口方式创建线程
new Thread(new MyThread1(),"thread-A").start();
// 使用实现Callable接口方式创建线程
// new Thread(new MyThread2(),"thread-A").start(); //错误演示,不能类比使用实现Runnable接口方式创建线程
FutureTask<Object> task = new FutureTask<>(new MyThread2());
// lambda表达式
FutureTask task2 = new FutureTask(() -> {
return 200;
});
new Thread(task1,"thread-B").start();
}
}
6.3 Callable接口设计模式
6.3.1 适配器模式
有些情况下,某些已有的类实现不能做接口上的更改,但是却要实现更多的功能,这个时候就可以使用适配器模式增加一个适配接口来完成一种设计上的“组合”用以最终实现用户需求。
6.3.2 Callable使用适配器模式
Callable无法想使用实现Runnable接口那样来创建线程(即Thread thread = new Thread(Runnable接口的实现类)
)。是因为采用了适配器模式。
Runnable接口有实现类FutureTask,而FutureTask构造函数可以传入Callable,由此使用了适配器模式完成了创建线程的任务。
6.4 FutureTask接口
public class FutureTask<V> extends nObject implements RunnableFuture<V>
可取消的异步计算。利用开始和取消计算的方法、查询计算是否完成的方法和获取计算结果的方法,此类提供了对 Future 的基本实现。仅在计算完成时才能获取结果;如果计算尚未完成,则阻塞 get 方法。一旦计算完成,就不能再重新开始或取消计算。
可使用 FutureTask 包装 Callable 或 Runnable 对象。因为 FutureTask 实现了 Runnable,所以可将 FutureTask 提交给 Executor 执行。
除了作为一个独立的类外,此类还提供了 protected 功能,这在创建自定义任务类时可能很有用。
未来任务,就是硕在不影响主线程的情况下,再去开启一个线程完成要完成的任务,最后进行汇总,且只需要汇总一次。若再需要结果时候可以直接拿到响应结果。
构造方法摘要 | |
---|---|
FutureTask(Callable<V> callable) | |
创建一个 FutureTask,一旦运行就执行给定的 Callable。 | |
FutureTask(Runnable runnable, V result) | |
创建一个 FutureTask,一旦运行就执行给定的 Runnable,并安排成功完成时 get 返回给定的结果 。 |
方法摘要 | |
---|---|
boolean | cancel(boolean mayInterruptIfRunning) |
试图取消对此任务的执行。 | |
protected void | done() |
当此任务转换到状态 isDone(不管是正常地还是通过取消)时,调用受保护的方法。 | |
V | get() |
如有必要,等待计算完成,然后获取其结果。 | |
V | get(long timeout, TimeUnit unit) |
如有必要,最多等待为使计算完成所给定的时间之后,获取其结果(如果结果可用)。 | |
boolean | isCancelled() |
如果在任务正常完成前将其取消,则返回 true。 | |
boolean | isDone() |
如果任务已完成,则返回 true。 | |
void | run() |
除非已将此 Future 取消,否则将其设置为其计算的结果。 | |
protected boolean | runAndReset() |
执行计算而不设置其结果,然后将此 Future 重置为初始状态,如果计算遇到异常或已取消,则该操作失败。 | |
protected void | set(V v) |
除非已经设置了此 Future 或已将其取消,否则将其结果设置为给定的值。 | |
protected void | setException(Throwable t) |
除非已经设置了此 Future 或已将其取消,否则它将报告一个ExecutionException,并将给定的 throwable 作为其原因。 |
6.5 Callable结合FutureTask使用
package callable;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
/**
* @author LWJ
* @date 2023/6/19
*/
// 实现Runnable接口
class MyThread1 implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"===" + "run...");
}
}
// 实现Callable接口
class MyThread2 implements Callable<Object> {
@Override
public Object call() throws Exception {
System.out.println(Thread.currentThread().getName()+"===" + "call...");
return 200;
}
}
public class CreateThreadDemo {
public static void main(String[] args) throws ExecutionException, InterruptedException {
// 使用实现Runnable接口方式创建线程
//new Thread(new MyThread1(),"thread-A").start();
// 使用实现Callable接口方式创建线程
// new Thread(new MyThread2(),"thread-A").start(); //错误演示,不能类比使用实现Runnable接口方式创建线程
FutureTask<Object> task1 = new FutureTask<>(new MyThread2());
// lambda表达式
FutureTask task2 = new FutureTask(() -> {
System.out.println(Thread.currentThread().getName() + " come in");
return 200;
});
new Thread(task1,"thread-B").start();
new Thread(task2,"thread-C").start();
while(!task2.isDone()){
System.out.println("wait...");
}
System.out.println(task2.get());
System.out.println("main over ... ");
}
}
- 输出
wait...
wait...
wait...
wait...
wait...
wait...
thread-B===call...
wait...
wait...
wait...
wait...
wait...
wait...
wait...
wait...
wait...
thread-C come in
wait...
200
main over ...
6.6 总结
在主线程中需要执行比较耗时的操作时,但又不想阻塞主线程时,可以把这些作业交给 Future 对象在后台完成, 当主线程将来需要时,就可以通过 Future对象获得后台作业的计算结果或者执行状态
- 一般 FutureTask 多用于耗时的计算,主线程可以在完成自己的任务后,再去获取结果
- 仅在计算完成时才能检索结果;如果计算尚未完成,则阻塞 get 方法。一旦计算完成,就不能再重新开始或取消计算。 get 方法而获取结果只有在计算完成时获取,否则会一直阻塞直到任务转入完成状态,然后会返回结果或者抛出异常。
- 只计算一次