线程池 submit()执行怎么获取到的返回值

Java创建线程有三种方法,1是继承Thread类,2是实现Runnable接口,3是实现Callable接口

1和2差不多,就是一个是继承关系,继承了Thread类就不能继承其它类的,会有困扰。而实现接口却可实现多个。

而且继承Thread类去实现的run方法实际还是Runnable中的run方法

前提

执行submit()方法会返回一个Future。这个future.get()可以阻塞获取值

demo- 21-40 行是创建一个线程池,重点关注42,49,57代码快

package study4;

import java.util.concurrent.*;

public class ThreadPoolDemo {
    public static void main(String[] args) {
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(2, 4,
                60, TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(10),
                new ThreadFactory() {
                    @Override
                    public Thread newThread(Runnable r) {
                        return new Thread(r, "线程");
                    }
                }, new RejectedExecutionHandler() {
                        @Override
                        public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
                            System.out.println("执行拒绝策略");
                            try {
                                throw new RejectedExecutionException();
                            } catch (Exception e) {
                                e.printStackTrace();
                            }
                        }
                }
        );

        Future<?> submit1 = threadPoolExecutor.submit(new Runnable() {
            @Override
            public void run() {
                System.out.println("submit1执行任务");
            }
        });

        Future<String> submit2 = threadPoolExecutor.submit(new Callable<String>() {
            @Override
            public String call() throws Exception {
                return "submit2执行了任务";
            }
        });

        final String[] res = {new String("有什么用?")};
        Future<String> submit3 = threadPoolExecutor.submit(new Runnable() {
            @Override
            public void run() {
                System.out.println("submit3执行了任务");
                res[0] = new String("123");
                return;
            }
        }, res[0]);

        try {
            System.out.println("submit2 get "+submit2.get());
            System.out.println("submit3 get "+ res[0]);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }

        threadPoolExecutor.shutdown();
    }
}

分析1和3 方法

直接放入实现Runnable接口的任务。

Future<?> submit1 = threadPoolExecutor.submit(new Runnable() {
            @Override
            public void run() {
                System.out.println("submit1执行任务");
            }
        });
 
 final String[] res = {new String("有什么用?")};
        Future<String> submit3 = threadPoolExecutor.submit(new Runnable() {
            @Override
            public void run() {
                System.out.println("submit3执行了任务");
                res[0] = new String("123");
                return;
            }
        }, res[0]);        
    

也就是submit(Runnable) 和 submit(Runnalbe,Object)一个带Object参数,一个不带。结果就是带Object的可以返回返回值。
源码查看

  public Future<?> submit(Runnable task) {
        if (task == null) throw new NullPointerException();
        RunnableFuture<Void> ftask = newTaskFor(task, null);
        execute(ftask);
        return ftask;
    }
    
    
    public <T> Future<T> submit(Runnable task, T result) {
        if (task == null) throw new NullPointerException();
        RunnableFuture<T> ftask = newTaskFor(task, result);
        execute(ftask);
        return ftask;
    }

可以看到,带不带都调用了RunnableFuture ftask = newTaskFor(task, result); 然后又执行了execute()方法
可以说,submit()此时和execute()方法此时区别就是RunnableFuture类。

看一下源码,发现此接口同时继承了Runnalbe和Future接口

public interface RunnableFuture<V> extends Runnable, Future<V> {
   
    void run();
}

其实,最终是落到了它的实现类FutureTask,作用就是将Runnable转为Callable,并且设置返回值。

看一下 newTaskFor(task, result);源码

protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) {
        return new FutureTask<T>(runnable, value);
    }
    
    public FutureTask(Runnable runnable, V result) {
        this.callable = Executors.callable(runnable, result);  //此行就可看出,将Runnable转为了callable
        this.state = NEW;      
    }
    
     public static <T> Callable<T> callable(Runnable task, T result) {
        if (task == null)
            throw new NullPointerException();
        return new RunnableAdapter<T>(task, result);
    }
    
    RunnableAdapter(Runnable task, T result) {
            this.task = task;
            this.result = result;
    }
    
    static final class RunnableAdapter<T> implements Callable<T> {
        final Runnable task;
        final T result;
        RunnableAdapter(Runnable task, T result) {
            this.task = task;
            this.result = result;
        }
        public T call() {
            task.run();
            return result;
        }
    }

这个RunnableAdapter实现了Callable,然后通过构造参数将runable封装起来。如果result不为null,代表此时runnable也需要返回值。

result是返回值,那么怎么获取到runnable中的返回值呢,就是Future怎么就可以通过get获取到?

debug一下流程

首先执行的是runnable。但是之前说RunnableAdapter将它封装为Callable方法,那么就是通过

        public T call() {
            task.run(); 
            return result;
        }

实现的。并且返回result。

Runnable结论

最终可以观察到,即使是Runnable也是通过了一些方法转化为Callable。或者说是被封装为Callable。

分析2

继续分析第二个代码块,也就是直接

  Future<String> submit2 = threadPoolExecutor.submit(new Callable<String>() {
            @Override
            public String call() throws Exception {
                return "submit2执行了任务";
            }
        });

这个是怎么将返回值让future.get()可以获取到的?
其实一看源代码,都一样,这个是直接将callable赋值给FutureTask

 public FutureTask(Callable<V> callable) {
        if (callable == null)
            throw new NullPointerException();
        this.callable = callable;
        this.state = NEW;       // ensure visibility of callable
    }
    this.callable = Executors.callable(runnable, result);  //此行就可看出,将Runnable转为了callable

之前的还需要封装

那么也就应该是FutureTask解决的返回值问题。
debug发现直接跳到了futureTask的run()方法

再这个方法中调用了之前封装或者原装的Callable的result = c.call();
call可以有返回值,result
然后调用一个set()方法,将result赋值给FutureTask中的private Object outcome;

public V get() throws InterruptedException, ExecutionException {
        int s = state;
        if (s <= COMPLETING)
            s = awaitDone(false, 0L);
        return report(s);
    }
    
      private V report(int s) throws ExecutionException {
        Object x = outcome;
        if (s == NORMAL)
            return (V)x;
        if (s >= CANCELLED)
            throw new CancellationException();
        throw new ExecutionException((Throwable)x);
    }
    
    //可以通过get方法得知,它就是获取的outcome的值

最终回答

这个只是一些表面的。但是主要流程也是这

回答问题:
线程池 submit()执行怎么获取到的返回值

如果是Runnable接口的任务,则先封装为Callable,然后通过FutureTask调用call方法,由
call方法调用run方法。run中可返回数据,就是给submit(runnable,Object) Object赋值

如果是Callable,则不封装,直接使用原装的callable。

返回值的话是call方法有返回值,这个返回值会被赋值给Future的一个私有属性。然后
程序中调用Future会获取到这个私有属性的值。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值