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 task1 =  new FutureTask(()->{
            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 task1 =  new FutureTask(()->{
            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 runAsync(Runnable runnable);
public static CompletableFuture runAsync(Runnable runnable, Executor executor);
public static  CompletableFuture supplyAsync(Supplier supplier);
public static  CompletableFuture supplyAsync(Supplier supplier, Executor executor);

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

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

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

计算完成时回调方法

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

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

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

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

方法体权限 public 和 返回值 CompletableFuture

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

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

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

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

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

线程串行化方法

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

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

public CompletionStage thenRun(Runnable action);
public CompletionStage thenRunAsync(Runnable action);
public CompletionStage 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 allOf(CompletableFuture... cfs);

public static CompletableFuture 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等;它们之间是同理。

你可能感兴趣的:(JAVA基础,java)