高效开发:java中Future的使用

Posted Java架构师(公众号:毛奇志)

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了高效开发:java中Future的使用相关的知识,希望对你有一定的参考价值。

一、前言

Future是java 1.5引入的一个interface,可以方便的用于异步结果的获取。

二、创建Future

Future代表的是异步执行的结果,意思是当异步执行结束之后,返回的结果将会保存在Future中。

那么我们什么时候会用到Future呢? 一般来说,当我们执行一个长时间运行的任务时,使用Future就可以让我们暂时去处理其他的任务,等长任务执行完毕再返回其结果。

经常会使用到Future的场景有:1. 计算密集场景。2. 处理大数据量。3. 远程方法调用等,都是耗时比较长的需要等待的任务。

接下来我们将会使用ExecutorService来创建一个Future。

// submit方法中,参数表示要执行的任务,返回值表示任务执行的结果
<T> Future<T> submit(Callable<T> task);

上面是ExecutorService中定义的一个submit方法,它接收一个Callable参数,并返回一个Future。

我们用一个线程来计算一个平方运算:

private ExecutorService executor
        = Executors.newSingleThreadExecutor();

public Future<Integer> calculate(Integer input) {
    return executor.submit(() -> {
        System.out.println("Calculating..."+ input);
        Thread.sleep(1000);
        return input * input;
    });
}

submit需要接受一个Callable参数,Callable需要实现一个call方法,并返回结果。这里我们使用lamaba表达式来简化这一个流程。

三、从Future获取结果

上面我们创建好了Future,接下来我们看一下怎么获取到Future的值。

   FutureUsage futureUsage=new FutureUsage();
    Future<Integer> futureOne = futureUsage.calculate(20);
    while(!futureOne.isDone()) {
        System.out.println("Calculating...");
        Thread.sleep(300);
    }
    Integer result = futureOne.get();

首先我们通过Future.isDone() 来判断这个异步操作是否执行完毕,如果完毕我们就可以直接调用futureOne.get()来获得Futre的结果。

这里futureOne.get()是一个阻塞操作,会一直等待异步执行完毕才返回结果。

如果我们不想等待,future提供了一个带时间的方法:

Integer result = futureOne.get(500, TimeUnit.MILLISECONDS);

如果在等待时间结束的时候,Future还有返回,则会抛出一个TimeoutException。

四、取消Future

如果我们提交了一个异步程序,但是想取消它, 则可以这样:

Future<Integer> futureTwo = futureUsage.calculate(4);

boolean canceled = futureTwo.cancel(true);

Future.cancel(boolean) 传入一个boolean参数,来选择是否中断正在运行的task。

如果我们cancel之后,再次调用get()方法,则会抛出CancellationException。

五、多线程环境中运行

如果有两个计算任务,先看下在单线程下运行的结果。

Future<Integer> future1 = futureUsage.calculate(10);
Future<Integer> future2 = futureUsage.calculate(100);

while (!(future1.isDone() && future2.isDone())) {
    System.out.println(
            String.format(
                    "future1 is %s and future2 is %s",
                    future1.isDone() ? "done" : "not done",
                    future2.isDone() ? "done" : "not done"
            )
    );
    Thread.sleep(300);
}

Integer result1 = future1.get();
Integer result2 = future2.get();

System.out.println(result1 + " and " + result2);

因为我们通过Executors.newSingleThreadExecutor()来创建的单线程池。所以运行结果如下:

Calculating...10
future1 is not done and future2 is not done
future1 is not done and future2 is not done
future1 is not done and future2 is not done
future1 is not done and future2 is not done
Calculating...100
future1 is done and future2 is not done
future1 is done and future2 is not done
future1 is done and future2 is not done
100 and 10000

如果我们使用Executors.newFixedThreadPool(2)来创建一个多线程池,则可以得到如下的结果:

calculating...10
calculating...100
future1 is not done and future2 is not done
future1 is not done and future2 is not done
future1 is not done and future2 is not done
future1 is not done and future2 is not done
100 and 10000

六、全部代码

6.1 Future的使用

这里我们会用到 futureUsage.calculate(20)执行 futureOne.isDone()等待 futureOne.get()得到。

package com.example.demo;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class FutureDemo {
    public static void main(String[] args) throws Exception{
        FutureUsage futureUsage=new FutureUsage();
        Future<Integer> futureOne = futureUsage.calculate(20);
        while(!futureOne.isDone()) {
            System.out.println("Main Calculating...");
            Thread.sleep(300);  // 里面1000毫秒处理时间,这里300毫秒询问一次,所以打印三次
        }
        Integer result = futureOne.get();  // 这是阻塞的,这里得不到答案,下面就无法打印
        System.out.println(result);
    }
}
class FutureUsage{
    private ExecutorService executor
            = Executors.newSingleThreadExecutor();

    public Future<Integer> calculate(Integer input) { // 这里通过submit得到一个Future实现对象
        return executor.submit(new Callable<Integer>() {
            @Override
            public Integer call() throws Exception {
                System.out.println("FutureUsage Calculating..."+ input);
                Thread.sleep(1000); // 这里等待1000毫秒,1秒  测试Main里面的while循环不断打印,因为 isDone 为false
                return input * input;
            }
        });
    }
}

输出结果:

Main Calculating...
FutureUsage Calculating...20
Main Calculating...
Main Calculating...
Main Calculating...
400

6.2 执行耗时任务,返回值放到Future里面,阻塞main线程

package com.example.demo;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class FutureDemo {
    public static void main(String[] args) throws Exception{
        FutureUsage futureUsage=new FutureUsage();
        // 执行一个需要等待的任务,将任务放到要给future对象里面
        Future<Integer> futureOne = futureUsage.calculate(20);
        // futrue.isDone()方法没什么用,因为get()方法不仅可以判断,还可以拿到结果
        Integer result = futureOne.get();  // 这是阻塞的,这里得不到答案,下面就无法打印
        System.out.println(result);
    }
}
class FutureUsage{
    private ExecutorService executor
            = Executors.newSingleThreadExecutor();

    public Future<Integer> calculate(Integer input) {
        return executor.submit(new Callable<Integer>() {
            @Override
            public Integer call() throws Exception {
                System.out.println("FutureUsage Calculating..."+ input); // 打印输入
                Thread.sleep(1000); // 这里等待1000毫秒,1秒  测试Main里面的while循环不断打印,因为 isDone 为false
                return input * input;
            }
        });
    }
}

6.3 线程池中只有一个线程

package com.example.demo;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class FutureDemo {
    public static void main(String[] args) throws Exception {
        FutureUsage futureUsage = new FutureUsage();
        // 给定两个任务,放到两个Future中
        Future<Integer> future1 = futureUsage.calculate(10);
        Future<Integer> future2 = futureUsage.calculate(100);
        // 取出两个future中的结果
        Integer result1 = future1.get();
        Integer result2 = future2.get();
        System.out.println(result1 + " and " + result2);
    }
}

class FutureUsage {
    private ExecutorService executor
            = Executors.newSingleThreadExecutor();

    public Future<Integer> calculate(Integer input) {
        return executor.submit(new Callable<Integer>() {
            @Override
            public Integer call() throws Exception {
                System.out.println("FutureUsage Calculating..." + input); // 打印输入
                Thread.sleep(1000); // 这里等待1000毫秒,1秒  测试Main里面的while循环不断打印,因为 isDone 为false
                return input * input;
            }
        });
    }
}
FutureUsage Calculating...10
FutureUsage Calculating...100
100 and 10000

6.4 线程池中有两个线程

package com.example.demo;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class FutureDemo {
    public static void main(String[] args) throws Exception {
        // 只有一个futureUsage对象,这个对象里面有一个,两个线程的线程池
        FutureUsage futureUsage = new FutureUsage();  
        // 给定两个任务,放到两个Future中
        Future<Integer> future1 = futureUsage.calculate(10);
        Future<Integer> future2 = futureUsage.calculate(100);
        // 取出两个future中的结果
        Integer result1 = future1.get();
        Integer result2 = future2.get();
        System.out.println(result1 + " and " + result2);
    }
}

class FutureUsage {
    // 这里新建一个有两个线程的线程池
    private ExecutorService executor
            = Executors.newFixedThreadPool(2);  

    public Future<Integer> calculate(Integer input) {
        return executor.submit(new Callable<Integer>() {
            @Override
            public Integer call() throws Exception {
                System.out.println("FutureUsage Calculating..." + input); // 打印输入
                Thread.sleep(1000); // 这里等待1000毫秒,1秒  测试Main里面的while循环不断打印,因为 isDone 为false
                return input * input;
            }
        });
    }
}

七、尾声

java中Future的使用,完成了。

天天打码,天天进步!!

以上是关于高效开发:java中Future的使用的主要内容,如果未能解决你的问题,请参考以下文章

高效Web开发的10个jQuery代码片段

Java 多线程中的Future

你可能不知道的JavaScript代码片段和技巧(下)

你可能不知道的JavaScript代码片段和技巧(上)

std::future::wait 应该使用这么多 CPU 吗?有没有更高效的调用?

Scala之Future