异步编程

1.概念

同步编程:线程等待返回
异步编程:

2.显式使用线程和线程池实现

2.1 显式使用

  • 1.实现Runnable接口
public class SyncExample {

    public static void main(String[] args) throws Exception {
        long start = System.currentTimeMillis();
        // 第一种
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    task1();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
        // Java8写法
//        Thread thread = new Thread( () -> {
//            try {
//                task1();
//            } catch (Exception e) {
//                e.printStackTrace();
//            }
//        }, "SyncExample");

        task2();
        // 同步等待1任务结束
        thread.join();
        System.out.println(System.currentTimeMillis() - start);
    }

    public static void task1(){
        try {
            Thread.sleep(2000);
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println("Task1");
    }

    public static void task2() {
        try {
            Thread.sleep(2000);
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println("Task2");
    }
}
  • 2.实现Thread类重写run方法
public class SyncExample2 {

    public static void main(String[] args) throws Exception {
        long start = System.currentTimeMillis();
        Thread thread = new Thread("Example"){
            @Override
            public void run() {
                try {
                    task1();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        };
        task2();
        // 同步等待1任务结束
        thread.join();
        System.out.println(System.currentTimeMillis() - start);
    }

    public static void task1(){
        try {
            Thread.sleep(2000);
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println("Task1");
    }

    public static void task2() {
        try {
            Thread.sleep(2000);
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println("Task2");
    }
}

问题:

  • 每当执行异步任务时,直接创建一个Thread,线程创建与销毁有开销,并且没有限制线程个数,应用线程池
  • Thread执行异步任务没有返回值,需要用Future
  • 每次都创建是命令式编程方式,需要声明式编程方法,即告诉程序我们要异步执行,但如何实现应该对我们透明

2.2 线程池实现

  • 无返回结果
public class SyncExample3ThreadPool {

    /**
     * 线程池核心线程个数为当前CPU核数
     */
    private final static int AVALIABLE_PROCESSORS = Runtime.getRuntime().availableProcessors();

    /**
     * 最大线程个数为CPU核数2倍
     * 拒绝策略为CallerRunsPolicy,当线程池任务饱和,执行拒绝策略不会丢弃新任务,使用调用线程执行
     * 使用命名的线程创建工厂来追溯业务
     */
    public static final ThreadPoolExecutor POOL_EXECUTOR = new ThreadPoolExecutor(AVALIABLE_PROCESSORS,
            AVALIABLE_PROCESSORS * 2, 1, TimeUnit.MINUTES,
            new LinkedBlockingQueue<>(5), new ThreadPoolExecutor.CallerRunsPolicy());

    public static void main(String[] args) throws Exception {
        long start = System.currentTimeMillis();
        POOL_EXECUTOR.execute( () -> {
            try {
                task1();
            } catch (Exception e) {
                e.printStackTrace();
            }
        });
        task2();
        System.out.println(System.currentTimeMillis() - start);
        // 挂起当前线程
        Thread.currentThread().join();
    }
}
  • 带返回值
    使用Callable类型任务提交到线程,返回Future对象,以阻塞的方式获取结果
public class SyncExample3Return {

    /**
     * 线程池核心线程个数为当前CPU核数
     */
    private final static int AVALIABLE_PROCESSORS = Runtime.getRuntime().availableProcessors();

    /**
     * 最大线程个数为CPU核数2倍
     * 拒绝策略为CallerRunsPolicy,当线程池任务饱和,执行拒绝策略不会丢弃新任务,使用调用线程执行
     * 使用命名的线程创建工厂来追溯业务
     */
    public static final ThreadPoolExecutor POOL_EXECUTOR = new ThreadPoolExecutor(AVALIABLE_PROCESSORS,
            AVALIABLE_PROCESSORS * 2, 1, TimeUnit.MINUTES,
            new LinkedBlockingQueue<>(5), new ThreadPoolExecutor.CallerRunsPolicy());

    public static void main(String[] args) throws Exception {
        // 必须阻塞线程才能获取结果
        Future<?> taskReturn = POOL_EXECUTOR.submit(new Callable<String>() {
            @Override
            public String call() throws Exception {
                return task1();
            }
        });
        // Lambda写法
//        Future<?> taskReturn = POOL_EXECUTOR.submit(() -> task1());
        System.out.println(taskReturn.get());
    }

    public static String task1(){
        try {
            Thread.sleep(2000);
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println("Task1");
        return "Task1";
    }
}

3.Future实现

JUC中,提供一些方法来检查计算结果是否已经完成,也提供获取计算结果的方法。

3.1 接口方法

  • get: 等待异步计算完成并返回结果
    如果当前任务计算未完成会阻塞,直到完成
    如果其他线程取消/中断,抛出异常
  • 带超时时间的get:
    任务没有被计算出来前不会一直阻塞,会等待timeout时间后抛异常
  • isDone: 任务是否正常完成
  • cancel: 取消任务
    已经完成或已经取消,失败
    没被执行,不会执行
    已经运行,根据mayIntereuptIfRunning决定是否中断,true中断
  • isCancelled:是否取消

3.2 FutureTask

可被取消的异步计算任务,实现Future接口
任务可以是Callable和Runnable类型
任务完成后不能重启,除非使用runAndReset方法
弊端:

  • 不能清楚地表达多个FutureTask之间关系
  • get方法还是会阻塞线程
public class FutureExample {

    public static String task1(){
        try {
            Thread.sleep(2000);
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println("Task1");
        return "Task1";
    }
    
    public static void main(String[] args) throws Exception {
        // 创建任务
        FutureTask<String> futureTask = new FutureTask<String>( () -> {
            String res = null;
            try {
               res =  task1();
            } catch (Exception e) {
                e.printStackTrace();
            }
            return res;
        });
        // 显式执行A
        Thread thread = new Thread(futureTask, "threadA");
        // 线程池执行
//        POOL_EXECUTOR.execute(futureTask);
        thread.start();;
        // 等待A完成
        String resA = futureTask.get();
        System.out.println(resA);
    }
}

3.3 FutureTask类详解

5.CompletableFuture

通过编程方式显式设置计算结果和状态以让任务结束的Future,并且作为CompletionStage(计算阶段),当它的计算完成时触发一个函数
多个线程调用一个CompletableFuture的complete,cancel方式时只有一个会成功

  • 显式使用
public class CompletableFutureTest {

    private final static int AVALIABLE_PROCESSORS = Runtime.getRuntime().availableProcessors();

    public static final ThreadPoolExecutor POOL_EXECUTOR = new ThreadPoolExecutor(AVALIABLE_PROCESSORS,
            AVALIABLE_PROCESSORS * 2, 1, TimeUnit.MINUTES,
            new LinkedBlockingQueue<>(5), new ThreadPoolExecutor.CallerRunsPolicy());

    public static void main(String[] args) throws Exception {
        CompletableFuture<String> future = new CompletableFuture<String>();

        POOL_EXECUTOR.execute( () -> {
            // 模拟计算
            try {
                Thread.sleep(3000);
            } catch (Exception e){
                e.printStackTrace();
            }
            // 设置计算结算到future
            future.complete("Hello World");
        });
        // 等待结果,complete完成后,由get而被阻塞的线程被激活
        System.out.println(future.get());
    }
}
  • 异步计算和结果转换
  • 基于runAsync
public class CompletableFutureNoRes {

    private final static int AVALIABLE_PROCESSORS = Runtime.getRuntime().availableProcessors();

    public static final ThreadPoolExecutor POOL_EXECUTOR = new ThreadPoolExecutor(AVALIABLE_PROCESSORS,
            AVALIABLE_PROCESSORS * 2, 1, TimeUnit.MINUTES,
            new LinkedBlockingQueue<>(5), new ThreadPoolExecutor.CallerRunsPolicy());

    public static void main(String[] args) throws Exception {
        runAsync();
    }

    public static void runAsync() throws Exception {
        // 默认情况下 使用JVM唯一的ForkJoinPool.commonPool()线程池执行
        CompletableFuture future = CompletableFuture.runAsync(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(2000);
                } catch (Exception e){
                    e.printStackTrace();
                }
                System.out.println("end");
            }
        });
        // 使用自己线程池
        CompletableFuture futureMy = CompletableFuture.runAsync(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(2000);
                } catch (Exception e){
                    e.printStackTrace();
                }
                System.out.println("end");
            }
        }, POOL_EXECUTOR);
        // 无返回值 结果为null
        System.out.println(future.get());
    }
}
  • 有返回值
public class CompletableFutureWithRes {
    private static final ThreadPoolExecutor bizPoolExecutor = new ThreadPoolExecutor(8, 8, 1, TimeUnit.MILLISECONDS,
            new LinkedBlockingQueue<>(10));

    // A完成后激活B 但是B拿不到A的结果
    public static void main(String[] args) throws Exception {
        // 1.基于supplyAsync 获取线程结果
        // 默认commonPool, 参数使用自己线程池
        CompletableFuture futureA = CompletableFuture.supplyAsync(new Supplier<String>() {
            @Override
            public String get() {
                // 执行任务
                System.out.println("futureA");
                return "Hello";
            }
        }, bizPoolExecutor);

        // 2.thenRun A完成后激活B 但是B拿不到A的结果,也无法get获取到结果
        CompletableFuture futureB = futureA.thenRun(new Runnable() {
            @Override
            public void run() {
                System.out.println("futureB");
            }
        });

        // 3.thenAccept A完成后激活B B可以拿到A的结果,也无法get获取到结果
        CompletableFuture futureC = futureA.thenAccept(new Consumer<String>() {
            @Override
            public void accept(String o) {
                System.out.println("A:" + o);
                System.out.println("futureC");
            }
        });

        // 4.thenApply B可以拿到A执行结果,可以获取get执行结果
        CompletableFuture futureD = futureA.thenApply(new Function<String, String>() {
            @Override
            public String apply(String o) {
                System.out.println("A:" + o);
                return "futureD";
            }
        });
        System.out.println(futureD.get());

        // 5.whenComplete 设置回调函数 不阻塞调用线程
        futureA.whenComplete(new BiConsumer<String, Throwable>() {
            @Override
            public void accept(String o, Throwable o2) {
                if (o2 == null) {
                    System.out.println("A:" + o);
                } else {
                    o2.printStackTrace();
                }
            }
        });
        // 挂起等待任务执行完毕
        Thread.currentThread().join();
    }
}
  • 多个任务
public class TwoCompletableFuture {

    // 多个CompletableFuture进行组合运算
    public static void main(String[] args) throws Exception {
        String ID = "ID1234";
        // 1.基于thenCompose 一个CF执行完毕 执行另一个
        CompletableFuture res = taskOne(ID).thenCompose(id -> taskTwo(id));
        System.out.println(res.get());

        // 2.基于thenCombine 俩个任务都完成后,使用这俩个参数
        CompletableFuture res2 = taskOne(ID).thenCombine(taskTwo("234"), (one, two) -> {
            return one + " " + two;
        });
        System.out.println(res2.get());

        // 3.基于allOf 多个并发任务都完成
        List<CompletableFuture<String>> futureList = new ArrayList<>();
        futureList.add(taskOne("1"));
        futureList.add(taskOne("2"));
        futureList.add(taskOne("3"));
        // 转换为一个
        CompletableFuture<Void> res3 = CompletableFuture.allOf(futureList.toArray(new CompletableFuture[futureList.size()]));
        System.out.println(res3.get());

        // 4.基于anyOf 等任一一个完成
        List<CompletableFuture<String>> futureList2 = new ArrayList<>();
        futureList.add(taskOne("1"));
        futureList.add(taskOne("2"));
        futureList.add(taskOne("3"));
        // 转换为一个
        CompletableFuture<Object> res4 = CompletableFuture.anyOf(futureList.toArray(new CompletableFuture[futureList.size()]));
        System.out.println(res4.get());
    }

    public static CompletableFuture<String> taskOne(String id){
        return CompletableFuture.supplyAsync(new Supplier<String>() {
            @Override
            public String get() {
                return id;
            }
        });
    }

    public static CompletableFuture<String> taskTwo(String id){
        return CompletableFuture.supplyAsync(new Supplier<String>() {
            @Override
            public String get() {
                return id + "My";
            }
        });
    }
}
  • 异常处理
public class ExceptionCompletableFuture {

    public static void main(String[] args) throws Exception {
        CompletableFuture<String> future = new CompletableFuture<String>();
        new Thread( () -> {
            try {
                throw new Exception();
            } catch (Exception e) {
                // 如果没有这个 get会一直阻塞
                future.completeExceptionally(e);
            }
        }).start();

//        System.out.println(future.get());
        // 出现异常返回默认值
        System.out.println(future.exceptionally( t -> "Exception").get());
    }

}
  • Stream流中使用
public class StreamExample {

    public static String myCall(String ip){
        System.out.println(ip);
        return ip + "Success";
    }

    public static void main(String[] args) {
        try {
            List<String> list = new ArrayList<>();
            for (int i = 0; i < 10; i++) {
                list.add("192.168.0." + i);
            }
            List<CompletableFuture<String>> futureList = list.stream().map(ip ->
                    CompletableFuture.supplyAsync(() -> myCall(ip))).collect(Collectors.toList());
            futureList.stream().forEach(r -> System.out.println(r));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

4.Spring框架中异步

4.1 TaskExecutor

public interface TaskExecutor {
  void execute(Runnable task)
}

Spring中的实现:

  • SimpleAsyncTaskExecutor:该实现不会复用线程,对于每个请求会新创建一个对应的线程来执行。
    它支持的并发限制将阻止任何超过限制的调用,通过setConcurrencyLimit方法来限制并发数,默认不限制并发数
  • SyncTaskExecutor:不会异步执行提交的任务,而是同步调用线程来执行,这种实现主要用户没有必要多线程进行处理的情况。
  • ConcurrencyTaskExecutor:
  • SimpleThreadPollTaskExecutor:
  • ThreadPoolTaskExecutor:
  • TimerTaskExecutor:

4.2 使用

4.2.1 SpringBoot使用TaskExecutor实现

  • 启动类
@EnableAsync
@SpringBootApplication
public class App
{
    public static void main( String[] args )
    {
        SpringApplication.run(App.class);
    }


    /**
     * 核心线程数10:线程池创建时初始化的线程数
     * 最大线程数20:线程池最大的线程数,只有在缓冲队列满了之后才会申请超过核心线程数的线程
     * 缓冲队列200:用来缓冲执行任务的队列
     * 允许线程的空闲时间60秒:超过了核心线程数之外的线程,在空闲时间到达之后会被销毁
     * 线程池名的前缀:设置好了之后可以方便我们定位处理任务所在的线程池
     * 线程池对拒绝任务的处理策略:此处采用了CallerRunsPolicy策略,当线程池没有处理能力的时候,该策略会直接在execute方法的调用线程中运行被拒绝的任务;如果执行程序已被关闭,则会丢弃该任务
     * 设置线程池关闭的时候等待所有任务都完成再继续销毁其他的Bean
     * 设置线程池中任务的等待时间,如果超过这个时候还没有销毁就强制销毁,以确保应用最后能够被关闭,而不是阻塞住
     */
    @EnableAsync
    @Configuration
    class TaskPoolConfig{
        @Bean("taskExecutor")
        public TaskExecutor taskExecutor(){
            ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
            // 核心进程数
            taskExecutor.setCorePoolSize(5);
            // 最大线程个数
            taskExecutor.setMaxPoolSize(10);
            // 超过线程个数的线程空闲多久被回收
            taskExecutor.setKeepAliveSeconds(60);
            // 缓存队列大小
            taskExecutor.setQueueCapacity(20);
            // 拒绝策略 CallerRunsPolicy:队列满了,且所有线程忙碌时,新任务不再异步执行,而是调用新线程来执行
            taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor .CallerRunsPolicy());
            taskExecutor.setThreadNamePrefix("TaskExecutor-");
            // 等任务执行完毕才退出
            taskExecutor.setWaitForTasksToCompleteOnShutdown(true);
            taskExecutor.setAwaitTerminationSeconds(60);
            taskExecutor.initialize();
            return taskExecutor;
        }
    }
}
  • AsyncExecutorExample
@Service
public class AsyncExecutorExample {

    @Resource
    private TaskExecutor taskExecutor;

    public void task(){
        for (int i = 0; i < 6; i++) {
            taskExecutor.execute(new MessagePrinter("Message" + i));
        }
    }

    /**
     * task任务执行完后不会退出因为不是守护线程,需要显示关闭
     * ThreadPoolTaskExecutor
     */
    public void shutdown(){
        if (taskExecutor instanceof ThreadPoolTaskExecutor){
            ((ThreadPoolTaskExecutor)taskExecutor).shutdown();
        }
    }

    private class MessagePrinter implements Runnable{
        private String message;

        public MessagePrinter(String message) {
            this.message = message;
        }

        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName() + ": " + message);
        }
    }
}
  • TestController
@RestController
public class TestController {

    @Autowired
    private AsyncExecutorExample asyncExecutorExample;

    @GetMapping("/test")
    @ResponseBody
    public void test() {
        asyncExecutorExample.task();
        asyncExecutorExample.shutdown();
    }
}

4.2.2 SpringBoot使用@Async实现

调用线程在含有调用@Async方法时立即返回,实际执行发生在Spring中的TaskExecutor异步处理线程中

  • AsyncAnnotationExample
@Service
public class AsyncAnnotationExample {

    /**
     * 指定线程池
     * 返回值必须为Future及其子类
     */
    @Async("bizExecutor")
    public void task(){
        for (int i = 0; i < 6; i++) {
            System.out.println(Thread.currentThread().getName() + ": " + i);
        }
    }

    /**
     * 有返回值
     * @return
     */
    @Async
    public CompletableFuture<String> taskWithRes(){
        CompletableFuture<String> res = new CompletableFuture<String>();
        System.out.println(Thread.currentThread().getName() + ": " + "Task");
        res.complete("success");
        return res;
    }

}
  • TestController
@GetMapping("/test")
    @ResponseBody
    public void test() {
        try {
            // 无返回值
            asyncAnnotationExample.task();
            // 有返回值
            CompletableFuture<String> res = asyncAnnotationExample.taskWithRes();
            res.whenComplete(new BiConsumer<String, Throwable>() {
                @Override
                public void accept(String s, Throwable throwable) {
                    if (throwable == null) {
                        System.out.println(s);
                    }
                }
            });
        } catch (Exception e) {
            e.printStackTrace();
        }

    }
  • MyAsyncUncaughtExceptionHandler
@Component
public class MyAsyncUncaughtExceptionHandler implements AsyncUncaughtExceptionHandler {

    /**
     * 当调用get方法时等待结果会抛出异常,对于void类型 无法被捕获且无法传输,可以使用该类处理该类型
     */
    @Override
    public void handleUncaughtException(Throwable ex, Method method, Object... params) {
        ex.printStackTrace();
    }
}

4.2.3 @Async原理

5.反应式编程中的异步

5.1 反应式编程

Reactive Programming 涉及数据流和变化传播的异步编程范式。

  • 及时响应式:系统及时作出响应
  • 回弹性:系统面临失败仍然保持及时响应性
  • 弹性:系统在不断变化的工作负载下仍然保持及时响应性
  • 消息驱动:依靠异步消息传递在组件之间建立边界,确保松耦合,隔离和位置透明性,边界提供将故障委派为消息投递出去的方法。

5.2 Reactive Streams规范

目的提供一个使用非阻塞回压功能对异步流进行处理的标准

5.3 基于RxJava实现异步

RxJava是Reactive Extensions的Java VM实现,是一个库,用于通过使用可观察序列来编写异步和基于事件的程序

  • 依赖
  <dependency>
    <groupId>io.reactivex.rxjava2</groupId>
    <artifactId>rxjava</artifactId>
    <version>2.2.10</version>
  </dependency>
  • 基本使用
public static void main(String[] args) {
    List<Person> list = new ArrayList<Person>();
    // 过滤与输出
    Flowable.fromArray(list.toArray(new Person[0]))
            .filter(p -> p.getAge() >= 10)
            .map(p -> p.getName())
            .subscribe(System.out::println);// 输出
}

RxJava操作运算符不能直接使用Threads或ExectutorServices进行异步处理,需要使用Schedulers来抽象统一API背后的并发调度线程池。

  • Schedulers.io:在动态变化的集合上运行类IO或阻塞操作
  • Schedulers.computation:在后台运行固定数量的专用线程来计算密集型工作
  • Schedulers.single: 顺序和FIFO方式在单个线程上运行
  • Schedulers.from(Executor): 将线程池包装到Schedulers中
    .observeOn(Scheduler.io()) // 切换到IO线程

5.4 基于Reactor实现

6.Spring WebFlux

7.中间件

7.1 Netty

7.2 Dubbo

7.3 Disruptor

7.4 Akka

7.5 RocketMQ

posted @ 2024-04-22 01:23  lwx_R  阅读(1)  评论(0编辑  收藏  举报