Java 异步编程

同步操作如果遇到耗时的方法,需要阻塞等待,如何解决阻塞等待的问题?可以将一些耗时的方法放到异步操作中。

1. Runnable 异步执行

第一步:实现Runnable接口

/**
 * 实现Runnable接口
 */
public class MyRunnable implements Runnable {
    /**
     * 重写run(),将耗时操作放到里面
     */
    @Override
    public void run() {
        try {
            // 这里睡眠5s来模拟耗时操作
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

第二步:启动新线程异步执行

public class Test1 {
    public static void main(String[] args) {
        ThreadPoolExecutor threadPoolExecutor = null;
        try {
            // 创建一个线程池,需要线程就从线程池获取,避免反复创建、销毁线程带来的开销
            threadPoolExecutor = new ThreadPoolExecutor(10, 20,
                    1, TimeUnit.MINUTES, new ArrayBlockingQueue(10));
            System.out.println("异步执行前");
            // 执行
            threadPoolExecutor.execute(new MyRunnable());

            System.out.println("异步执行后");
        } finally {
            if (threadPoolExecutor != null) {
                threadPoolExecutor.shutdown();
            }
        }
    }
}

执行结果:从打印结果可以看出,并不需要等异步执行完才往下执行。

异步执行前
异步执行后
开始异步
异步结束

Process finished with exit code 0

2. Callable 异步执行

Runnable 虽然可以实现异步执行,但是 Runnable 对于异步执行不会有返回结果。如果需要异步执行的返回结果,则需要采用 Callable 实现异步执行。

第一步:实现Callable接口

/**
 * 实现Callable接口,指定的泛型就是返回的结果类型
 */
public class MyCallable implements Callable<String> {
    // 重写call(),将耗时操作放在里面
    @Override
    public String call() throws Exception {
        try {
            System.out.println("开始异步");
            // 这里睡眠5s来模拟耗时操作
            Thread.sleep(5000);
            System.out.println("异步结束");
            return "成功";
        } catch (InterruptedException e) {
            e.printStackTrace();
            return "失败";
        }
    }
}

第二步:启动新线程异步执行

public class Test1 {
    public static void main(String[] args) {
        ThreadPoolExecutor threadPoolExecutor = null;
        try {
            // 创建一个线程池,需要线程就从线程池获取,避免反复创建、销毁线程带来的开销
            threadPoolExecutor = new ThreadPoolExecutor(10, 20,
                    1, TimeUnit.MINUTES, new ArrayBlockingQueue(10));
            System.out.println("异步执行前");
            // 执行
            Future<String> submit = threadPoolExecutor.submit(new MyCallable());

            System.out.println("异步执行后");
        } finally {
            if (threadPoolExecutor != null) {
                threadPoolExecutor.shutdown();
            }
        }
    }
}

 执行结果:从打印结果可以看出,并不需要等异步执行完才往下执行。

异步执行前
异步执行后
开始异步
异步结束

Process finished with exit code 0

如果我们需要从Future中获取异步执行的结果,调用Future的get(),但是这个方法会阻塞等待直至异步执行完毕。

public class Test1 {
    public static void main(String[] args) {
        ThreadPoolExecutor threadPoolExecutor = null;
        try {
            // 创建一个线程池,需要线程就从线程池获取,避免反复创建、销毁线程带来的开销
            threadPoolExecutor = new ThreadPoolExecutor(10, 20,
                    1, TimeUnit.MINUTES, new ArrayBlockingQueue(10));
            System.out.println("异步执行前");
            // 执行
            Future<String> submit = threadPoolExecutor.submit(new MyCallable());
            // 获取异步执行的结果
            String result = submit.get();
            System.out.println("异步执行的结果:" + result);

            System.out.println("异步执行后");
        } catch (ExecutionException | InterruptedException e) {
            e.printStackTrace();
        } finally {
            if (threadPoolExecutor != null) {
                threadPoolExecutor.shutdown();
            }
        }
    }
}

执行结果:

异步执行前
开始异步
异步结束
异步执行的结果:成功
异步执行后

Process finished with exit code 0

3. CompletableFuture 异步执行

Runnable + Thread 可以实现异步但是没有返回值;Callable + Thread 可以实现异步并且有返回值,但是获取返回值会等待阻塞直至异步结束。  

因此,JDK8 中引入了 CompletableFuture,它实现了 Future 接口和 CompletionStage。Future 让它具备接受异步执行结果的能力;CompletionStage 用于异步执行中的阶段处理,提供了一系列的方法。 

3.1 CompletionStage方法介绍

3.1.1 方法分类

一、产出型:用上一个阶段的结果作为指定函数的参数,执行函数会产生新的结果。这一类接口方法名中基本都含apply,参数是(Bi)Function类型。

二、消费型:用上一个阶段的结果作为指定操作的参数,执行指定的操作,但不对阶段结果产生影响。这一类方法名中基本都含accept,参数是(Bi)Consumer类型。

三、不产出型不消费:不依据上一个阶段的执行结果,只要上一个阶段正常完成,才执行指定的操作,且不对阶段的结果产生影响。这一类方法名中基本含run,参数是Runnable类型。

3.1.2 方法展示

public class Test1 {
    public static void main(String[] args) {
        ThreadPoolExecutor threadPoolExecutor = null;
        try {
            // 创建一个线程池,需要线程就从线程池获取,避免反复创建、销毁线程带来的开销
            threadPoolExecutor = new ThreadPoolExecutor(10, 20,
                    1, TimeUnit.MINUTES, new ArrayBlockingQueue(10));
            System.out.println("异步执行前");

            // supplyAsync() 开启一个异步操作
            // thenAccept() 当异步操作结束,会把异步执行结果传给它,不会阻塞后面的操作
            CompletableFuture.supplyAsync(() -> {
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                return "异步执行结果";
            }, threadPoolExecutor).thenAccept(System.out::println);

            System.out.println("异步执行后");
        }  finally {
            if (threadPoolExecutor != null) {
                threadPoolExecutor.shutdown();
            }
        }
    }
}
异步执行前
异步执行后
异步执行结果

Process finished with exit code 0

CompletableFuture 提供了非常多的方法,可以满足不同的使用场景,大家可以结合自己的需求去调用不同的方法。这里只是给大家抛砖引玉,写了一个简单的示例。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值