Java多线程-线程池ThreadPoolExecutor的submit返回值Future

本文为转载,原文为:https://blog.csdn.net/qq_25806863/article/details/71214033

一般使用线程池执行任务都是调用的execute方法,这个方法定义在Executor接口中:

public interface Executor {
    void execute(Runnable command);
}

这个方法是没有返回值的,而且只接受Runnable。

那么像得到线程的返回值怎嘛办呢?

在ExecutorService接口中能找到这个方法:

<T> Future<T> submit(Callable<T> task);
<T> Future<T> submit(Runnable task, T result);
Future<?> submit(Runnable task);

这个方法接收两种参数,Callable和Runnable。返回值是Future。

下面具体看一下这些是什么东西。

Callable和Runnable
先看一下两个接口的定义:

Callable

public interface Callable<V> {
  V call() throws Exception;
}

Runnable

interface Runnable {
  public abstract void run();
}

和明显能看到区别:

Callable能接受一个泛型,然后在call方法中返回一个这个类型的值。而Runnable的run方法没有返回值
Callable的call方法可以抛出异常,而Runnable的run方法不会抛出异常。
Future
返回值Future也是一个接口,通过他可以获得任务执行的返回值。

定义如下:

public interface Future<V> {
    boolean cancel(boolean var1);

    boolean isCancelled();

    boolean isDone();

    V get() throws InterruptedException, ExecutionException;

    V get(long var1, TimeUnit var3) throws InterruptedException, ExecutionException, TimeoutException;
}

其中的get方法获取的就是返回值。

来个例子

submit(Callable task)

public class Main {
    public static void main(String[] args) throws InterruptedException, ExecutionException {
        ExecutorService executor = Executors.newFixedThreadPool(2);
        //创建一个Callable,3秒后返回String类型
        Callable myCallable = new Callable() {
            @Override
            public String call() throws Exception {
                Thread.sleep(3000);
                System.out.println("calld方法执行了");
                return "call方法返回值";
            }
        };
        System.out.println("提交任务之前 "+getStringDate());
        Future future = executor.submit(myCallable);
        System.out.println("提交任务之后,获取结果之前 "+getStringDate());
        System.out.println("获取返回值: "+future.get());
        System.out.println("获取到结果之后 "+getStringDate());
    }
    public static String getStringDate() {
        Date currentTime = new Date();
        SimpleDateFormat formatter = new SimpleDateFormat("HH:mm:ss");
        String dateString = formatter.format(currentTime);
        return dateString;
    }
}

通过executor.submit提交一个Callable,返回一个Future,然后通过这个Future的get方法取得返回值。

看一下输出:

提交任务之前 12:13:01
提交任务之后,获取结果之前 12:13:01
calld方法执行了 获取返回值: call方法返回值
获取到结果之后 12:13:04

get()方法的阻塞性

通过上面的输出可以看到,在调用submit提交任务之后,主线程本来是继续运行了。但是运行到future.get()的时候就阻塞住了,一直等到任务执行完毕,拿到了返回的返回值,主线程才会继续运行。

这里注意一下,他的阻塞性是因为调用get()方法时,任务还没有执行完,所以会一直等到任务完成,形成了阻塞。

任务是在调用submit方法时就开始执行了,如果在调用get()方法时,任务已经执行完毕,那么就不会造成阻塞。

下面在调用方法前先睡4秒,这时就能马上得到返回值。

System.out.println("提交任务之前 "+getStringDate());
Future future = executor.submit(myCallable);
System.out.println("提交任务之后 "+getStringDate());
Thread.sleep(4000);
System.out.println("已经睡了4秒,开始获取结果 "+getStringDate());
System.out.println("获取返回值: "+future.get());
System.out.println("获取到结果之后 "+getStringDate());

提交任务之前 12:36:04
提交任务之后 12:36:04
calld方法执行了 已经睡了4秒,开始获取结果 12:36:08
获取返回值: call方法返回值
获取到结果之后 12:36:08

可以看到吗,因为睡了4秒,任务已经执行完毕,所以get方法立马就得到了结果。

同样的原因,submit两个任务时,总阻塞时间是最长的那个。

例如,有两个任务,一个3秒,一个5秒。

Callable myCallable = new Callable() {
    @Override
    public String call() throws Exception {
        Thread.sleep(5000);
        System.out.println("calld方法执行了");
        return "call方法返回值";
    }
};
Callable myCallable2 = new Callable() {
    @Override
    public String call() throws Exception {
        Thread.sleep(3000);
        System.out.println("calld2方法执行了");
        return "call2方法返回值";
    }
};
System.out.println("提交任务之前 "+getStringDate());
        Future future = executor.submit(myCallable);
        Future future2 = executor.submit(myCallable2);
        System.out.println("提交任务之后 "+getStringDate());
        System.out.println("开始获取第一个返回值 "+getStringDate());
        System.out.println("获取返回值: "+future.get());
        System.out.println("获取第一个返回值结束,开始获取第二个返回值 "+getStringDate());
        System.out.println("获取返回值2: "+future2.get());
        System.out.println("获取第二个返回值结束 "+getStringDate());

输出

提交任务之前 14:14:47
提交任务之后 14:14:48
开始获取第一个返回值 14:14:48
calld2方法执行了
calld方法执行了
获取返回值: call方法返回值
获取第一个返回值结束,开始获取第二个返回值 14:14:53
获取返回值2:call2方法返回值
获取第二个返回值结束 14:14:53

获取第一个结果阻塞了5秒,所以获取第二个结果立马就得到了。

submit(Runnable task)

因为Runnable是没有返回值的,所以如果submit一个Runnable的话,get得到的为null:

Runnable myRunnable = new Runnable() {
    @Override
    public void run() {
        try {
            Thread.sleep(2000);
            System.out.println(Thread.currentThread().getName() + " run time: " + System.currentTimeMillis());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
};

Future future = executor.submit(myRunnable);
        System.out.println("获取的返回值: "+future.get());

输出为:

pool-1-thread-1 run time: 1493966762524
获取的返回值: null

submit(Runnable task, T result)

虽然submit传入Runnable不能直接返回内容,但是可以通过submit(Runnable task, T result)传入一个载体,通过这个载体获取返回值。这个其实不能算返回值了,是交给线程处理一下。

先新建一个载体类Data:

public static class Data {
    String name;
    String sex;

public String getName() {
    return name;
}

public void setName(String name) {
    this.name = name;
}

public String getSex() {
    return sex;
}

public void setSex(String sex) {
    this.sex = sex;
}

}

然后在Runnable的构造方法中传入:

static class MyThread implements Runnable {
    Data data;

public MyThread(Data name) {
    this.data = name;
}

@Override
public void run() {
    try {
        Thread.sleep(2000);
        System.out.println("线程  执行:");
        data.setName("新名字");
        data.setSex("新性别");
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

}

然后调用:

Data data = new Data();
Future<Data> future = executor.submit(new MyThread(data), data);
System.out.println("返回的结果  name: " + future.get().getName()+", sex: "+future.get().getSex());
System.out.println("原来的Data  name: " + data.getName()+", sex: "+data.getSex());

输出:

线程 执行:

返回的结果 name: 新名字, sex: 新性别
原来的Data name: 新名字, sex: 新性别

1
2
3
发现原来的data也变了。

get(long var1, TimeUnit var3)

前面都是用的get()方法获取返回值,那么因为这个方法是阻塞的,有时需要等很久。所以有时候需要设置超时时间。

get(long var1, TimeUnit var3)这个方法就是设置等待时间的。

如下面的任务需要5秒才能返回结果:

Callable myCallable = new Callable() {
    @Override
    public String call() throws Exception {
        Thread.sleep(5000);
        return "我是结果";
    }
};

使用get:

Future future1 = executor.submit(myCallable);
System.out.println("开始拿结果 "+getStringDate());
System.out.println("返回的结果是: "+future1.get()+ " "+getStringDate());
System.out.println("结束拿结果 "+getStringDate());

输出是:

开始拿结果 16:00:43
返回的结果是: 我是结果 16:00:48
结束拿结果 16:00:48

现在要求最多等3秒,拿不到返回值就不要了,所以用get(long var1, TimeUnit var3)这个方法

方法的第一个参数是长整形数字,第二个参数是单位,跟线程池ThreadPoolExecutor的构造方法里一样的。

Future future1 = executor.submit(myCallable);
System.out.println("开始拿结果 "+getStringDate());
try {
    System.out.println("返回的结果是: "+future1.get(3, TimeUnit.SECONDS)+ " "+getStringDate());
} catch (TimeoutException e) {
    e.printStackTrace();
    System.out.println("超时了 "+getStringDate());
}
System.out.println("结束拿结果 "+getStringDate());

然后输出是

过了三秒就抛出超时异常了,主线程继续运行,不会再继续阻塞。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
Java 中,我们可以使用 ThreadPoolExecutor 类来管理线程池并执行多个任务。ThreadPoolExecutor 类实现了 ExecutorService 接口,可以用于提交和执行任务。 要使用 ThreadPoolExecutor.submit() 方法提交多个任务,可以按照以下步骤进行操作: 1. 创建 ThreadPoolExecutor 对象,并指定线程池的核心线程数、最大线程数、线程空闲时间等参数。例如: ```java ThreadPoolExecutor executor = new ThreadPoolExecutor( corePoolSize, maximumPoolSize, keepAliveTime, TimeUnit.SECONDS, new LinkedBlockingQueue<>() ); ``` 2. 创建任务并将其封装为 Runnable 或 Callable 对象。Runnable 是一个没有返回的任务,而 Callable 可以返回一个结果。例如: ```java Runnable task1 = new MyRunnableTask(); Callable<Integer> task2 = new MyCallableTask(); ``` 3. 使用 submit() 方法将任务提交给线程池。例如: ```java executor.submit(task1); executor.submit(task2); ``` 4. 线程池会自动调度空闲的线程来执行提交的任务。 注意:submit() 方法会返回一个 Future 对象,可以使用它来获取任务的执行结果或取消任务的执行。如果不关心任务的执行结果,可以忽略 submit() 方法返回Future 对象。 另外,记得在程序结束时关闭线程池,以释放资源: ```java executor.shutdown(); ``` 这是一个基本的示例,你可以根据实际需求调整线程池的参数和任务的实现方式。希望能对你有所帮助!如果你有更多问题,请继续提问。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值