java基础巩固-宇宙第一AiYWM:为了维持生计,多高(多线程与高并发)_Part10~整起(成天说同步和异步,你也落实一下嘛,至少落实到代码中呀)

PART1:同步与异步
java基础巩固-宇宙第一AiYWM:为了维持生计,多高(多线程与高并发)_Part10~整起(成天说同步和异步,你也落实一下嘛,至少落实到代码中呀)_第1张图片
java基础巩固-宇宙第一AiYWM:为了维持生计,多高(多线程与高并发)_Part10~整起(成天说同步和异步,你也落实一下嘛,至少落实到代码中呀)_第2张图片

  • 同步:你要泡茶,那你烧水的同时,能不能去先洗洗茶具啥的,别楞在那呀,同步就是水不开我不动,敌不动我不动
  • 异步:我不楞在那,水不开我先干点其他的
    • 异步编程是让程序并发运行的一种手段。它允许多个事件同时发生,当程序调用需要长时间运行的方法时,它不会阻塞当前的执行流程,程序可以继续运行。
      • 核心思路:采用多线程优化性能,将串行操作变成并行操作。异步模式设计的程序可以显著减少线程等待,从而在高吞吐量场景中,极大提升系统的整体性能,显著降低时延。

PART2:常见的异步编程实现方式

  • 常见的异步编程实现方式
    • 方式一:直接继承 Thread类 是创建异步线程最简单的方式【首先,创建 Thread 子类,普通类或匿名内部类方式;然后创建子类实例;最后通过 start()方法启动线程。】
      • 当然如果每次都创建一个 Thread 线程,频繁的创建、销毁,浪费系统资源。我们可以采用线程池
        java基础巩固-宇宙第一AiYWM:为了维持生计,多高(多线程与高并发)_Part10~整起(成天说同步和异步,你也落实一下嘛,至少落实到代码中呀)_第3张图片
        @Bean(name = "executorService")
        public ExecutorService downloadExecutorService() {
            return new ThreadPoolExecutor(20, 40, 60, TimeUnit.SECONDS, new ArrayBlockingQueue<>(2000),
                    new ThreadFactoryBuilder().setNameFormat("defaultExecutorService-%d").build(),
                    (r, executor) -> log.error("defaultExecutor pool is full! "));
        }
        
      • 启动线程的三种基本方式有相关讲述
        • 上面三种线程启动方式咱们也可以知道,有时候有些业务不仅仅要执行过程,还要获取执行结果。那么只用继承Thread达到多线程并行处理就不够了,Java 从 1.5 版本开始,提供了 Callable 和 Future,可以在任务执行完毕之后得到任务执行结果。也提供了其他功能,如:取消任务、查询任务是否完成等
          java基础巩固-宇宙第一AiYWM:为了维持生计,多高(多线程与高并发)_Part10~整起(成天说同步和异步,你也落实一下嘛,至少落实到代码中呀)_第4张图片
        • 一个例子:
          public class CallableAndFuture {
          
              public static ExecutorService executorService = new ThreadPoolExecutor(4, 40, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(1024), new ThreadFactoryBuilder().setNameFormat("demo-pool-%d").build(), new ThreadPoolExecutor.AbortPolicy());
          
              static class MyCallable implements Callable<String> {
                  @Override
                  public String call() throws Exception {
                      return "异步处理,Callable 返回结果";
                  }
              }
          
              public static void main(String[] args) {
                  Future<String> future = executorService.submit(new MyCallable());
                  try {
                      System.out.println(future.get());
                  } catch (Exception e) {
                      // nodo
                  } finally {
                      executorService.shutdown();
                  }
              }
          }
          
        • 除了Future那种实现方式之外,FutureTask 实现了 RunnableFuture 接口,则 RunnableFuture 接口继承了 Runnable 接口和 Future 接口,所以可以将 FutureTask 对象作为任务提交给 ThreadPoolExecutor 去执行,也可以直接被 Thread 执行;又因为实现了 Future 接口,所以也能用来获得任务的执行结果。启动线程的三种基本方式中ctrl+F搜索FutureTask
    • 异步框架 CompletableFuture:
      java基础巩固-宇宙第一AiYWM:为了维持生计,多高(多线程与高并发)_Part10~整起(成天说同步和异步,你也落实一下嘛,至少落实到代码中呀)_第5张图片
      • Future 类通过 get() 方法阻塞等待获取异步执行的运行结果,性能比较差。JDK1.8 中,Java 提供了 CompletableFuture 类,它是基于异步函数式编程。相对阻塞式等待返回结果,CompletableFuture 可以通过回调的方式来处理计算结果,实现了异步非阻塞,性能更优
        java基础巩固-宇宙第一AiYWM:为了维持生计,多高(多线程与高并发)_Part10~整起(成天说同步和异步,你也落实一下嘛,至少落实到代码中呀)_第6张图片
      • CompletableFuture 提供了非常丰富的 API,大约有 50 种处理串行,并行,组合以及处理错误的方法。
    • SpringBoot 注解 @Async
      • 除了硬编码的异步编程处理方式,SpringBoot 框架还提供了 注解式 解决方案,以 方法体 为边界,方法体内部的代码逻辑全部按异步方式执行
      • 在 Spring Boot 应用中使用 @Async的步骤:
        java基础巩固-宇宙第一AiYWM:为了维持生计,多高(多线程与高并发)_Part10~整起(成天说同步和异步,你也落实一下嘛,至少落实到代码中呀)_第7张图片
        • 首先,使用 @EnableAsync 启用异步注解
          java基础巩固-宇宙第一AiYWM:为了维持生计,多高(多线程与高并发)_Part10~整起(成天说同步和异步,你也落实一下嘛,至少落实到代码中呀)_第8张图片
        • 自定义线程池:
          @Configuration
          @Slf4j
          public class ThreadPoolConfiguration {
          
              @Bean(name = "defaultThreadPoolExecutor", destroyMethod = "shutdown")
              public ThreadPoolExecutor systemCheckPoolExecutorService() {
          
                  return new ThreadPoolExecutor(3, 10, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(10000), new ThreadFactoryBuilder().setNameFormat("default-executor-%d").build(), (r, executor) -> log.error("system pool is full! "));
              }
          }
          
        • 在异步处理的方法上添加注解 @Async【用 @Async 注解标记的方法,称为异步方法】,当对 execute 方法执行调用时,通过自定义的线程池defaultThreadPoolExecutor 异步化执行 execute 方法
          @Service
          public class AsyncServiceImpl implements AsyncService {
          	//在异步处理的方法上添加注解 @Async ,当对 execute 方法执行调用时,通过自定义的线程池 defaultThreadPoolExecutor 异步化执行 execute 方法
              @Async("defaultThreadPoolExecutor")
              public Boolean execute(Integer num) {
                  System.out.println("线程:" + Thread.currentThread().getName() + " , 任务:" + num);
                  return true;
              }
          }
          
    • Spring ApplicationEvent 事件
      • 事件机制在一些大型项目中被经常使用,Spring 专门提供了一套事件机制的接口,满足了架构原则上的解耦。ApplicationContext 通过 ApplicationEvent 类【ApplicationEvent 是由 Spring 提供的所有 Event 类的基类】和 ApplicationListener 接口进行事件处理。如果将实现 ApplicationListener 接口的 bean 注入到上下文中,则每次使用 ApplicationContext 发布 ApplicationEvent 时,都会通知该 bean。本质上,这是标准的观察者设计模式
        • Spring 框架的事件机制默认是同步阻塞的。只是在代码规范方面做了解耦,有较好的扩展性,但底层还是采用同步调用方式。如果想实现异步调用,我们需要手动创建一个 SimpleApplicationEventMulticas
          @Component
          public class SpringConfiguration {
          
              @Bean
              public SimpleApplicationEventMulticaster applicationEventMulticaster(@Qualifier("defaultThreadPoolExecutor") ThreadPoolExecutor defaultThreadPoolExecutor) {
                  SimpleApplicationEventMulticaster simpleApplicationEventMulticaster = new SimpleApplicationEventMulticaster();
                  simpleApplicationEventMulticaster.setTaskExecutor(defaultThreadPoolExecutor);
                  return simpleApplicationEventMulticaster;
              }
          
          }
          
          • 涉及到SimpleApplicationEventMulticaster 这个我们自己实例化的 Bean 与系统默认的加载顺序时是这样的,处理逻辑在 AbstractApplicationContext#initApplicationEventMulticaster 方法中,容器会通过 beanFactory 查找是否有自定义的 Bean,如果没有,容器会自己 new 一个 SimpleApplicationEventMulticaster 对象注入到容器中
      • 使用步骤:
        • 首先,自定义业务事件子类,继承自 ApplicationEvent,通过泛型注入业务模型参数类。相当于 MQ 的消息体。
          java基础巩固-宇宙第一AiYWM:为了维持生计,多高(多线程与高并发)_Part10~整起(成天说同步和异步,你也落实一下嘛,至少落实到代码中呀)_第9张图片
        • 然后,编写事件监听器。ApplicationListener 接口是由 Spring 提供的事件订阅者必须实现的接口,我们需要定义一个子类,继承 ApplicationListener。相当于 MQ 的消费端
          @Component
          public class OrderEventListener implements ApplicationListener<OrderEvent> {
              @Override
              public void onApplicationEvent(OrderEvent event) {
          
                  System.out.println("【OrderEventListener】监听器处理!" + JSON.toJSONString(event.getSource()));
          
              }
          }
          
        • 最后,发布事件,把某个事件告诉所有与这个事件相关的监听器。相当于 MQ 的生产端。
          java基础巩固-宇宙第一AiYWM:为了维持生计,多高(多线程与高并发)_Part10~整起(成天说同步和异步,你也落实一下嘛,至少落实到代码中呀)_第10张图片
    • 消息队列:翻翻项目经验那里,这块了解的不多

巨人的肩膀:
java并发编程之美
java并发编程实战
网络编程
腾讯云社区各位老师
B战各位做视频的老师
SpringForAll
微观技术老师
极客时间的《Java 并发编程实战》

你可能感兴趣的:(多线程,到底有多多,高并发,到底有多高,java,异步,异步编程)