Java获取线程结果

Callable接口+FutureTask开发

开始阶段通过Callable和FutureTask获取结果,为了异步执行之后获取线程结果。 主线程不中断继续执行,开启子线程异步去执行其他操作。

使用步骤

  • 创建Callable接口的实现类,并实现call()方法,该call()方法将作为线程执行体,并且有返回值。  
  • 创建Callable实现类的实例,使用FutureTask类来包装Callable对象,该FutureTask对象封装了该Callable对象的call()方法的返回值。  
  • 使用FutureTask对象作为Thread对象的target创建并启动新线程。   
  • 调用FutureTask对象的get()方法来获得子线程执行结束后的返回值

优缺点

  • 优点:异步/多线程,显著提高程序的执行效率
  • 缺点:future.get会阻塞线程。虽然可以get(【传入超时时间】),但是也会抛出异常,不优雅。所以最好要放在主线程执行完。
 public static void main(String[] args) throws ExecutionException, InterruptedException, TimeoutException {
        FutureTask<String> task1 =  new FutureTask<String>(()->{
            try{ TimeUnit.MILLISECONDS.sleep(5000);}catch(InterruptedException e) {e.printStackTrace();}
            return "task1 over";
        });

        Thread t1 =  new Thread(task1,"t1");
        t1.start();
//        System.out.println(task1.get()); //调用get(),就会阻塞,不见不散,非要等到结果才会离开
        System.out.println(task1.get(3,TimeUnit.SECONDS));
        System.out.println(Thread.currentThread().getName()+"----忙其他任务了");
    }

isDone() [判断是否完成]轮询 容易引起cpu空转,耗费更多的资源

  public static void main(String[] args) throws ExecutionException, InterruptedException, TimeoutException {
        FutureTask<String> task1 =  new FutureTask<String>(()->{
            try{ TimeUnit.MILLISECONDS.sleep(5000);}catch(InterruptedException e) {e.printStackTrace();}
            return "task1 over";
        });

        Thread t1 =  new Thread(task1,"t1");
        t1.start();
//        System.out.println(task1.get()); //调用get(),就会阻塞,不见不散,非要等到结果才会离开
//        System.out.println(task1.get(3,TimeUnit.SECONDS));
        System.out.println(Thread.currentThread().getName()+"----忙其他任务了");

//        System.out.println(task1.get());
        while (true){
            if(task1.isDone()){
                System.out.println(task1.get());
                break;
            }else{
                try{ TimeUnit.MILLISECONDS.sleep(300);}catch(InterruptedException e) {e.printStackTrace();}
                System.out.println("正在处理中.......");
            }
        }
    }

CompletableFuture使用

创建方式

CompletableFuture 提供了四个静态方法来创建一个异步操作。

public static CompletableFuture<Void> runAsync(Runnable runnable);
public static CompletableFuture<Void> runAsync(Runnable runnable, Executor executor);
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier);
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier, Executor executor);

说明:没有指定Executor的方法会使用 ForkJoinPool.commonPool() 作为它的线程池执行异步代码。如果指定线程池,则使用指定的线程池运行。

①、runXxxx都是没有返回结果的,supplyXxx都是可以获取返回结果的

②、可以传入自定义的线程池,否则就用默认的线程池。

计算完成时回调方法

当CompletableFuture的计算结果完成,或者抛出异常的时候,可以执行特定的Action。主要是下面的方法:

注意下面CompletableFuture的回调方法参数是BiConsumer,包含两个参数,当返回值是null或者没有异常的情况下,都会接收。允许null!

public interface BiConsumer<T, U> {
    void accept(T var1, U var2);

    default BiConsumer<T, U> andThen(BiConsumer<? super T, ? super U> after) {
        Objects.requireNonNull(after);
        return (l, r) -> {
            this.accept(l, r);
            after.accept(l, r);
        };
    }
}

方法体权限 public 和 返回值 CompletableFuture

 whenComplete(BiConsumer<? super T,? super Throwable> action);
方法完成后的感知处理
返回相同的结果或例外,这一阶段的新completionstage,
这个阶段完成时,执行特定动作的结果和异常,这个阶段。 
 

 whenCompleteAsync(BiConsumer<? super T,? super Throwable> action);
返回相同的结果或例外,这一阶段的新completionstage,
这个阶段完成时,执行特定动作执行给定的操作这一阶段的默认的异步执行设施,
其结果和异常,这个阶段作为参数。

 whenCompleteAsync(BiConsumer<? super T,? super Throwable> action,
  Executor executor);
返回相同的结果或例外,这一阶段的新completionstage,
这个阶段完成时,执行使用所提供的遗嘱执行人,
给出的行动与结果和异常,这个阶段作为参数。 

public CompletableFuture<T> exceptionally(Function<Throwable,? extends T> fn);
方法出现异常后的处理,出现异常返回默认值
返回一个新的completablefuture已经完成与给定值。 

handle((t,u)
 跟whenComplete基本一致,区别在于handle的回调方法有返回值,能够重新设置返回值

线程串行化方法

  • thenApply 方法:当一个线程依赖另一个线程时,获取上一个任务返回的结果,并返回当前任务的返回值。
  • thenAccept方法:消费处理结果。接收任务的处理结果,并消费处理,无返回结果。
  • thenRun方法:只要上面的任务执行完成,就开始执行thenRun,只是处理完任务后,执行 thenRun的后续操作
public <U> CompletableFuture<U> thenApply(Function<? super T,? extends U> fn)
public <U> CompletableFuture<U> thenApplyAsync(Function<? super T,? extends U> fn)
public <U> CompletableFuture<U> thenApplyAsync(Function<? super T,? extends U> fn, Executor executor)
当一个线程依赖另一个线程时,获取上一个任务返回的结果,**并返回当前任务的返回值**。(有返回值)

public CompletionStage<Void> thenAccept(Consumer<? super T> action);
public CompletionStage<Void> thenAcceptAsync(Consumer<? super T> action);
public CompletionStage<Void> thenAcceptAsync(Consumer<? super T> action,Executor executor);
消费处理结果。接收任务的处理结果,并消费处理,**无返回结果。

public CompletionStage<Void> thenRun(Runnable action);
public CompletionStage<Void> thenRunAsync(Runnable action);
public CompletionStage<Void> thenRunAsync(Runnable action,Executor executor);
只要上面的任务执行完成,就开始执行thenRun,**只是处理完任务后,执行 thenRun的后续操作

每一个方法都对应了三种操作。带有Async默认是异步执行的。这里所谓的异步指的是不在当前线程内执行。带有参数Executor executor的则用指定的线程池方案,不指定的话则用默认的ForkJoinPool.commonPool()。

说明:(Function fn)

T : 上一个任务返回结果的类型

​ U:当前任务的返回值的类型

任务组合【两任务组合(两个必须执行完成的情况加) 】

两个任务必须都完成,触发该任务。

thenCombine:组合两个future,获取两个future任务的返回结果,并返回当前任务的返回值

thenAcceptBoth:可以感知前两个结果,组合两个future,获取两个future任务的返回结果,然后处理任务,没有返回值。

runAfterBoth:无法感知前两个结果,组合两个future,不需要获取future的结果,只需两个future处理完任务后,处理该任务

任务组合【两任务组合(一个必须执行完成的情况加) 】

当两个任务中,任意一个future任务完成的时候,执行任务。

applyTOEither:两个任务有一个执行完成,获取它的返回值,处理任务并有新的返回值。

acceptEither:两个任务有一个执行完成,获取它的返回值,处理任务,没有新的返回值。

runAfterEither:两个任务有一个执行完成,不需要获取 future的结果,处理任务,也没有返回值。

多任务组合

public static CompletableFuture<Void> allOf(CompletableFuture<?>... cfs);

public static CompletableFuture<Object> anyOf(CompletableFuture<?>... cfs);

说明:

allOf:等待所有任务完成

anyOf:只要有一个任务完成

获取任务结果的方法

// 如果完成则返回结果,否则就抛出具体的异常
public T get() throws InterruptedException, ExecutionException 
 // 最大时间等待返回结果,否则就抛出具体异常
public T get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException
 // 完成时返回结果值,否则抛出unchecked异常。为了更好地符合通用函数形式的使用,如果完成此 CompletableFuture所涉及的计算引发异常,则此方法将引发unchecked异常并将底层异常作为其原因
public T join()
 // 如果完成则返回结果值(或抛出任何遇到的异常),否则返回给定的 valueIfAbsent。
public T getNow(T valueIfAbsent)
 // 如果任务没有完成,返回的值设置为给定值
public boolean complete(T value)
 // 如果任务没有完成,就抛出给定异常
public boolean completeExceptionally(Throwable ex) 

注意点 async的方法

没有传入自定义线程池,都用默认线程池ForkJoinPool。

传入了一个自定义线程池,执行第一个任务的时候,传入了一个自定义线程池;thenRun方法执行第二次,则第二个任务和第一个任务共用一个线程池。thenRunAsync执行第二个任务,第一个任务是使用的自己传入的线程池,第二个任务使用的是ForkJoinPool线程池。

如果处理速度太快,系统优化切换原则,直接使用main线程;

其他如:thenAccept和thenAcceptAsync,thenApply和thenApplyAsync等;它们之间是同理。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java中,多线程获取返回结果的常用方法是使用`Future`和`Callable`接口。 首先,你需要创建一个实现了`Callable`接口的类,该类负责执行具体的任务,并返回结果。例如: ```java import java.util.concurrent.Callable; public class MyCallable implements Callable<Integer> { @Override public Integer call() throws Exception { // 执行具体的任务逻辑 // 返回结果 return 42; } } ``` 然后,你可以使用`ExecutorService`来提交任务并获取`Future`对象,它代表了异步计算的结果。例如: ```java import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; public class Main { public static void main(String[] args) throws Exception { ExecutorService executorService = Executors.newFixedThreadPool(1); // 提交任务并获取Future对象 Future<Integer> future = executorService.submit(new MyCallable()); // 在需要结果的地方调用future.get()方法,该方法会阻塞直到任务完成并返回结果 int result = future.get(); System.out.println("结果:" + result); executorService.shutdown(); } } ``` 在上述代码中,我们使用`submit()`方法将`MyCallable`对象提交给线程池,并得到了一个`Future`对象。然后,我们通过调用`get()`方法来获取任务的执行结果。注意,`get()`方法会阻塞当前线程,直到任务完成并返回结果。 这样,你就可以通过多线程获取任务的返回结果了。当然,你也可以通过`Future`对象的其他方法来判断任务是否完成、取消任务等。具体使用方法可以参考Java官方文档。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值