在我们java多线程中,我想做一件事儿,但是我又不想影响主线程的执行,很多铁子都会想到用异步任务完成,这个时候我们的主角FutureTask就登场了。
FutureTask提供了对Future的基本实现,是一个可取消的异步计算,可以调用方法去开始和取消一个计算,可以查询计算是否完成并且获取计算结果。只有当计算完成时才能获取到计算结果,一旦计算完成,计算将不能被重启或者被取消,除非调用runAndReset方法。同时他也实现了Runnable接口,因此FutureTask交由Executor执行,可以直接用线程调用执行;
先给大家介绍2个新的接口,Future可以理解成是一个规范,FutureTask是实现了它,而Callable是需要用到的参数类型
1、了解了上面的2个接口后,这里我们去做一个案例,我要创建一个异步任务然后进行异步计算,要求返回一个计算值,且主线程继续执行,不会因为子线程的业务影响到主线程;
//1、创建一个异步任务 传递一个Callable的接口的参数,5秒后返回一个1024//使用的是函数式编程,这里的参数就是Callable类型的FutureTask task = new FutureTask<>(() -> {TimeUnit.SECONDS.sleep(5);return 1024;});//2、创建一个线程,把异步任务丢进去new Thread(task, "t1").start();//3、主线程继续执行System.out.println("程序运行执行业务中...");//4、获取异步任务里面的返回值System.out.println(task.get());//5、结束提醒System.out.println("运行结束...");
2、上面注释说到了这个参数就是Callable类型的,这个可以从FutureTask的构造方法得到,所以这也是上面为啥要先介绍一下的原因:
public FutureTask(Callable callable) {if (callable == null)throw new NullPointerException();this.callable = callable;this.state = NEW; // ensure visibility of callable}
3、上面的案例我们看一下效果,在控制台里面,主程序执行业务输出语句并没有因为子线程的执行而延迟;
4、但是,发现了一个问题,“运行结束...”这一句话是在1024输出后才输出的,所以从某种意义上造成了阻塞的现象出现,这和我们说到的高并发就相违背了。在这里的解决办法,就是将子线程的值输出这句代码放到最后进行执行,能治标不治本的解决阻塞问题,代码如下:
public static void main(String[] args) throws ExecutionException, InterruptedException, TimeoutException {//1、创建一个异步任务FutureTask task = new FutureTask<>(() -> {TimeUnit.SECONDS.sleep(5);return 1024;});//2、创建一个线程,把异步任务丢进去new Thread(task, "t1").start();//3、主线程继续执行System.out.println("程序运行执行业务中...");//5、结束提醒System.out.println("运行结束...");//4、获取异步任务里面的返回值System.out.println(task.get());}
5、那么出现了一个新问题,我这个主程序需要异步的这个子任务执行完成后才能结束,举个例子:铁子们在工作中调用其他程序的api,是这样等他无限的执行业务,然后获取值再做自己的业务吗,比如我这里是5s,我就必须等它5s,最终因为工作没完成,或者效率低下,岂不是只有自己背着包包走人了,所以,我们想控制一下这个时间,在规定时间范围内,等他响应该怎么用呢,解决办法如下:
System.out.println(task.get());
//用下面的代码替换上面get()
System.out.println(task.get(2,TimeUnit.SECONDS));
6、通过替换,可以发现会报错,这个是因为规定时间内,子线程没返回数据,那肯定不是咱们主线程的问题了;
public static void main(String[] args) throws ExecutionException, InterruptedException, TimeoutException {//1、创建一个异步任务FutureTask task = new FutureTask<>(() -> {TimeUnit.SECONDS.sleep(5);return 1024;});//2、创建一个线程,把异步任务丢进去new Thread(task, "t1").start();//3、主线程继续执行System.out.println("程序运行中...");//获取异步任务里面的返回值
// System.out.println(task.get());
// System.out.println(task.get(2,TimeUnit.SECONDS));//轮询获取while (true) {TimeUnit.SECONDS.sleep(1);if (task.isDone()) {System.out.println(task.get());break;} else {System.out.println("GGG");}}//结束提醒System.out.println("运行结束...");}
1、应对异步任务的完成时间,我希望它完成了可以告诉我,也就是我们常说的回调通知
2、将2个或者多个异步计算合成一个异步计算,这几个异步计算相互独立,同时第二个又依赖于第一个的结果
3、当Future集合中某个任务最快结束时,返回结果。
......
Java并发编程—CompletableFuture的介绍和使用_小魏快起床的博客-CSDN博客