Java 并发之Future模式(异步)

异步和同步

异步

  • 异步的含义是计算机多线程的异步处理。与同步处理相对,异步处理不用阻塞当前线程来等待处理完成,而是允许后续操作,直至其它线程将处理完成,并回调通知此线程,获取此线程的结果。
  • 但此处需要明确的是:异步与多线程与并行不是同一个概念。

    同步的时候无法异步,但异步的过程中可以有同步
    并发的执行时间间隔中,也可能会变成并行

同步

  • 线程有可能和其他线程共享一些资源,比如,内存,文件,数据库等。
  • 当多个线程同时读写同一份共享资源的时候,可能会引起冲突。这时候,我们需要引入线程“同步”机制,即各位线程之间要有个先来后到,不能一窝蜂挤上去抢作一团。
  • 线程同步的真实意思和字面意思恰好相反。线程同步的真实意思,其实是“排队”:几个线程之间要排队,一个一个对共享资源进行操作,而不是同时进行操作。

Java并发包中的Future接口实现异步

何谓Future接口

见名知意:在(future)未来会完成任务,在未来(future)获得任务的结果

image.png

  • Future表示异步计算的结果。 提供了检查计算是否完成、等待计算完成以及检索计算结果的方法。
  • 结果只能在计算完成后使用get方法检索,必要时阻塞,直到它准备好。
  • 取消由cancel方法执行。 提供了其他方法来确定任务是正常完成还是被取消。 一旦计算完成,就不能取消计算。 如果为了可取消性而想使用Future但不提供可用结果,则可以声明Future<?>形式的类型并返回null作为底层任务的结果。

看看Future接口中的方法

image.png

见名知意,不赘述了,就看看get()方法即可

  • V get()方法

image.png

  • 如有必要,等待计算完成,然后检索其结果。
  • 返回值:计算结果

  • V get(long timeout, TimeUnit unit)方法

image.png

  • 如有必要,最多等待给定的计算完成时间,然后检索其结果(如果可用)。
  • 参数:
    • timeout – 等待的最长时间
    • unit – 超时参数的时间单位
  • 返回值:计算结果
  • 抛出:
    • CancellationException – 如果计算被取消
    • ExecutionException – 如果计算抛出异常
    • InterruptedException – 如果当前线程在等待时被中断
    • TimeoutException – 如果等待超时

既然是接口就有实现类

image.png

132个实现类!!!

FutureTask<V>进行讲解

image.png

  • 成员变量和构造方法

image.png

关于FutureTask<V>类的样例

public class FutureTest {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        Callable<Integer> callable = ()->{
            System.out.println("pre Task");
            int randomNumber = new Random().nextInt(500);
            Thread.sleep(2000);
            System.out.println("post Execution");
            return randomNumber;
        };
        FutureTask<Integer> futureTask = new FutureTask<>(callable);
        new Thread(futureTask).start();//FutureTask继承了Runable接口,所以可以作为参数
        System.out.println("Thread has start");
        System.out.println(futureTask.get());//会导致主线程阻塞,因为sleep 2000
        System.out.println("main over");
    }
}
复制代码

但是Future接口有缺点

  • 它的get()方法,如果异步任务没有完成就调用get()方法会导致线程阻塞
  • 虽然get方法有一个重载的超时参数的方法,但是,如果任务超时就不能再获取结果了,而会抛出异常
  • 在实际开发中较少使用

使用CompletableFuture来避免Future接口的缺点

随意举一些CompletableFuture的样例

image.png

  • 代码
public class CompletableFutureT {
    public static void main(String[] args) throws InterruptedException {

        String result = CompletableFuture
                .supplyAsync(() -> "hello") //提供
                .thenApplyAsync(value -> value + " world")//应用
                .join();//完成时返回结果
        System.out.println(result);
        System.out.println("=========分割线========");
        CompletableFuture
                 //提供,参数是Supplier,不接收参数返回结果,
                .supplyAsync(() -> "Kitty Guy")
                //然后消费,参数是Consumer,接收参数数不返回结果
                .thenAcceptAsync(value -> System.out.println("welcome " + value));
        Thread.sleep(2);//貌似上面的操作有有点慢
        System.out.println("=========分割线========");
        String result2 = CompletableFuture.supplyAsync(() -> {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "hello";
        })
                .thenCombine(CompletableFuture.supplyAsync(() -> " world"), (s1, s2) -> s1 + s2)
                .join();
        System.out.println(result2);
    }

}
复制代码

image.png

  • 下面的是样例中用到的方法

image.png

image.png

image.png

CompletableFuture解决Futureget方法的阻塞问题样例

  • 代码
public class CompletableFutureT {
    public static void main(String[] args) throws InterruptedException {
                 Runnable runnable =()->{
             try {
                 Thread.sleep(2000);
                 System.out.println("Runable 睡眠结束");
             } catch (InterruptedException e) {
                 e.printStackTrace();
             }
         };
        CompletableFuture<Void> completableFuture2 = CompletableFuture.runAsync(runnable);//任务1
        CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(()->{//任务2
            try {
                Thread.sleep(1000);
                System.out.println("supplier 睡眠结束");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "来自异步任务的字符串";
        });
        //回调,当任务完成就执行whenComplete参数里面的方法
        completableFuture.whenComplete(((res, throwable) -> System.out.println(res+" 任务执行完") ));
        completableFuture2.whenComplete(((res, throwable) -> System.out.println("Runable 任务执行完") ));
        System.out.println("主线程结束");//会直接输出,不会阻塞
        Thread.sleep(5000);//需要主线程存活,不然异步任务会被关闭
    }

}
复制代码

image.png

image.png

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享