Java实现异步的几种方式

这篇具有很好参考价值的文章主要介绍了Java实现异步的几种方式。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

  1. 普通线程实现异步,但频繁创建、销毁线程比较耗资源,所以一般交给线程池执行
    //创建需要异步执行的逻辑
    public class AsyncThread implements Runnable{
        @Override
        public void run() {
            System.out.println("异步线程开始");
            long start = System.currentTimeMillis();
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            long end = System.currentTimeMillis();
            System.out.println("异步线程:" + Thread.currentThread().getName() + "结束,耗时:" + (end - start));
        }
    }
    
    //在业务中进行调用
    @GetMapping("/thread")
    public String asyncThread(){
        ThreadPoolExecutor threadPool = new ThreadPoolExecutor(3, 3, 5, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(10), new ThreadPoolExecutor.AbortPolicy());
        long start = System.currentTimeMillis();
        //自己的业务代码。。。
        AsyncThread asyncThread = new AsyncThread();
        threadPool.execute(asyncThread);
        long end = System.currentTimeMillis();
        return "返回,耗时:" + (end - start);
    }
    

    结果:Java实现异步的几种方式

  2. Future异步

    和普通线程实现异步区别不大,只是使用Future是要获取执行后的返回值

    //创建具有返回值的任务
    public class CallableThread implements Callable {
        @Override
        public String call() throws Exception {
            long start = System.currentTimeMillis();
            StopWatch stopWatch = new StopWatch();
            stopWatch.start();
            System.out.println("callable任务开始执行:" + start);
            TimeUnit.SECONDS.sleep(2);
            System.out.println();
            stopWatch.stop();
            System.out.println("stopWatch.prettyPrint------");
            System.out.println(stopWatch.prettyPrint());
            System.out.println("stopWatch.shortSummary------");
            System.out.println(stopWatch.shortSummary());
            System.out.println("stopWatch.getTotalTimeMillis------");
            System.out.println(stopWatch.getTotalTimeMillis());
            return "call执行结束 ";
        }
    }
    
    //在业务中进行调用
    public String threadFuture(){
        ThreadPoolExecutor threadPool = new ThreadPoolExecutor(3, 3, 5, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(10), new ThreadPoolExecutor.AbortPolicy());
        long start = System.currentTimeMillis();
        CallableThread callableThread = new CallableThread();
        Future<String> submit = threadPool.submit(callableThread);
        try {
            //在获取返回值时会阻塞主线程
            String s = "";
            s = submit.get();
            System.out.println(s);
        } catch (Exception e) {
            System.out.println("线程运行发生错误" + e.getMessage());
            throw new RuntimeException(e);
        }
        long end = System.currentTimeMillis();
        return "接口返回,耗时:" + (end - start);
    }
    

    结果:Java实现异步的几种方式

  3. Spring的@Async异步
    • 使用@Async注解实现异步的前提是需要在启动类上标注@EnableAsync来开启异步配置

    • 配置线程池(@Async默认情况下用的是SimpleAsyncTaskExecutor线程池,该线程池不是真正意义上的线程池,使用此线程池无法实现线程重用,每次调用都会新建一条线程。若系统中不断的创建线程,最终会导致系统占用内存过高,引发OutOfMemoryError错误)

      /**
       * 线程池配置,可以配置多个线程池
       * @Async注解,默认使用系统自定义线程池,可在项目中设置多个线程池,在异步调用的时候,指明需要调用的线程池名称
       * 比如:@Async("线程池1")
       */
      @Configuration
      public class ExecutorConfig {
          /**
           * 自定义线程池
           */
          @Bean("myExecutor")
          public Executor taskExecutor(){
              System.out.println("系统最大线程数:" + Runtime.getRuntime().availableProcessors());
              ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
              threadPoolTaskExecutor.setCorePoolSize(8);//核心线程数
              threadPoolTaskExecutor.setMaxPoolSize(16);//最大线程数
              threadPoolTaskExecutor.setQueueCapacity(1000);//配置队列容量
              threadPoolTaskExecutor.setKeepAliveSeconds(60);//空闲线程存活时间
              threadPoolTaskExecutor.setThreadNamePrefix("myExecutor-");//线程名字前缀
              return threadPoolTaskExecutor;
          }
      }
      
    • 编写异步方法的逻辑,异步方法所在的类需要被Spring管理

      @Service
      public class AsyncServiceImpl implements AsyncService {
          @Override
          @Async("myExecutor")
          public void sendMsg() {
              System.out.println("进入异步方法");
              System.out.println("当前线程名称:" + Thread.currentThread().getName());
              try {
                  TimeUnit.SECONDS.sleep(2);
              } catch (InterruptedException e) {
                  throw new RuntimeException(e);
              }
              System.out.println("异步方法执行完成");
          }
      
          /**
           * 具有返回值的异步方法,返回类型为Future,返回时new 一个AsyncResult对象,其中参数为返回的内容
           * @return
           */
          @Override
          @Async("myExecutor")
          public Future<String> sendMsgFuture() {
              System.out.println("进入future异步方法");
              try {
                  TimeUnit.SECONDS.sleep(2);
              } catch (InterruptedException e) {
                  throw new RuntimeException(e);
              }
              return new AsyncResult<>("future异步方法执行完成");
          }
      }
      
    • 在业务逻辑中调用

      @GetMapping("/asyncMethod")
      public String asyncMethod(){
          System.out.println("aaa");
          System.out.println("调用异步方法");
          asyncService.sendMsg();
          System.out.println("bbb");
          return "asyncMethod方法返回";
      }
      

      调用没有返回值的异步方法结果:Java实现异步的几种方式

      @GetMapping("/asyncFutureMethod")
      public String asyncFutureMethod(){
          System.out.println("aaa");
          Future<String> stringFuture = asyncService.sendMsgFuture();
          System.out.println("bbb");
          try {
          System.out.println(stringFuture.get());//get方法会阻塞主线程
          } catch (Exception e) {
          throw new RuntimeException(e);
          }
          return "asyncfutureMethod方法返回";
      }
      

      调用有返回值的异步方法结果:Java实现异步的几种方式

  4. Spring的ApplicationEvent事件实现异步
    • 定义事件,继承ApplicationEvent类

      public class MessageEvent extends ApplicationEvent {
          @Getter
          private String message;
      
          public MessageEvent(Object source, String message) {
              super(source);
              this.message = message;
          }
      }
      
    • 定义监听器(需要被Spring管理)

      使用@EventListener注解写在方法上定义一个监听器,即事件被触发时执行的方法(默认是同步执行,可以使用@Async注解标注为异步执行),支持多个监听器监听同一个事件。

      @Component
      public class MessageEventHandler {
      
          //@Async
          @EventListener
          public void handleLoginEvent(LoginEvent event){
              System.out.println("接受到LoginEvent事件");
              try {
                  TimeUnit.SECONDS.sleep(2);
              } catch (InterruptedException e) {
                  throw new RuntimeException(e);
              }
              System.out.println(event.getUsername());
              System.out.println("LoginEvent事件处理完成");
          }
      
          //@Async
          @EventListener
          public void handleMessageEvent(MessageEvent event){
              System.out.println("接受到MessageEvent事件");
              try {
                  TimeUnit.SECONDS.sleep(2);
              } catch (InterruptedException e) {
                  throw new RuntimeException(e);
              }
              System.out.println(event.getMessage());
              System.out.println("MessageEvent事件处理完成");
          }
      }
      
    • 定义事件发布者(触发事件的)(需要被Spring管理)

      实现ApplicationEventPublisherAware接口

      @Component
      public class EventPublisher implements ApplicationEventPublisherAware {
          private ApplicationEventPublisher publisher;
      
          @Override
          public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
              this.publisher = applicationEventPublisher;
          }
      
          public void publish(ApplicationEvent event){
              if (event instanceof MessageEvent){
                  System.out.println("开始发布MessageEvent事件:" + ((MessageEvent) event).getMessage());
              } else if (event instanceof LoginEvent) {
                  System.out.println("开始发布LoginEvent事件:" + ((LoginEvent) event).getUsername());
              }
              //发布事件
              publisher.publishEvent(event);
              System.out.println("事件发布结束");
          }
      
      }
      
    • 业务代码执行时触发事件

      @GetMapping("/pubEvent")
      public String publishEvent(){
          System.out.println("业务逻辑开始");
          eventPublisher.publish(new MessageEvent(this,"testEvent"));
          System.out.println("业务逻辑结束");
          return "发布成功";
      }
      

      执行结果:Java实现异步的几种方式

      由控制台打印可以发现现在事件监听器方法的执行是同步的,如果需要异步执行,在监听器方法上加个@Async注解即可,但使用Async注解的前提是在启动类上标注@EnableAsync注解来开启异步配置

      使用@Async注解后执行结果:Java实现异步的几种方式

      可以看到监听器中的打印在最后了,证明是异步执行的文章来源地址https://www.toymoban.com/news/detail-759990.html

到了这里,关于Java实现异步的几种方式的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 线程间实现通信的几种方式

    线程间通信的模型有两种:共享内存和消息传递,下面介绍的都是围绕这两个来实现 有两个线程A和B,B线程向一个集合里面依次添加元素“abc”字符串,一共添加10次,当添加到第五次的时候,希望线程A能够收到线程B的通知,然后B线程执行相关的业务操作 Object类提供了线程

    2024年02月15日
    浏览(68)
  • Qt 多线程的几种实现方式

    Qt多线程的实现方式有: 1. 继承QThread类,重写run()方法 2. 使用moveToThread将一个继承QObject的子类移至线程,内部槽函数均在线程中执行 3. 使用QThreadPool,搭配QRunnable(线程池) 4. 使用QtConcurrent(线程池) 为什么要用线程池? 创建和销毁线程需要和OS交互,少量线程影响不大,

    2024年02月15日
    浏览(43)
  • Java如何实现下载文件的几种方式

    版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 本文链接:https://blog.csdn.net/Boy_Martin/article/details/126058565

    2024年02月13日
    浏览(70)
  • 创建线程的几种方式

    线程和进程的区别: 进程是操作系统进行资源分配的最小单元。 线程是操作系统进行任务分配的最小单元,线程隶属于进程。 如何开启线程? 1、继承Thread类,重写run方法。 2、实现Runnable接口,实现run方法。 3、实现Callable接口,实现call方法。通过FutureTask创建一个线程,获

    2024年02月03日
    浏览(57)
  • Java生成二维码的几种实现方式

    本文将基于Spring Boot介绍两种生成二维码的实现方式,一种是基于Google开发工具包,另一种是基于Hutool来实现; 下面我们将基于Spring Boot,并采用两种方式实现二维码的生成,对于每一种方式还提供两种类型的二维码返回形式,即:物理文件 和 图片响应流 一、基于Google开发

    2024年02月17日
    浏览(44)
  • Java实现字符串排序的几种方式

    创建实体类(此处引入了lombok) 一、使用List集合中自带的sort方法(字符串的位数保持一致,不一致的情况可以在左边补0,也可以使用String.format()方法补全) 1、在对象排序中使用 2、在字符串排序中使用 二、使用Stream流(字符串的位数保持一致,不一致的情况可以在左边补

    2024年02月11日
    浏览(54)
  • 【Java 干货教程】Java实现分页的几种方式详解

    无论是自我学习中,还是在工作中,固然会遇到 与前端搭配实现分页的功能 ,发现有几种方式,特此记录一下。 这种情况也是有的,(根据业务场景且仅仅只能用于 数据量少 的情况)。即后端不做任何数据的限制,直接把全部数据返回给前端,前端通过组件实现分页,筛选等

    2024年02月02日
    浏览(49)
  • java实现调用http请求的几种常见方式

    ------ Oracle中文开发者社区 ------ 如果你想要学习编程,关注本博客,持续获得技术支持,持续获得技术咨询 java开发·企业官方账号 Oracle中国官方账号 Java中国管理部 全网粉丝30万+ 华为云享专家 阿里专家博主 CSDN内容合伙人 CSDN原力计划作者 51CTO专家博主 CSDN博客V账号 毕业于四川

    2024年02月04日
    浏览(55)
  • Java实现HTTP请求的几种方式-HttpURLConnection(一)

    在实际开发过程中,我们经常需要调用对方提供的接口或测试自己写的接口是否合适。很多项目都会封装规定好本身项目的接口规范,所以大多数需要去调用对方提供的接口或第三方接口(短信、天气等)。 准备两个项目: 项目A: 服务提供者 项目B:服务消费者 在项目A中

    2024年02月16日
    浏览(44)
  • JAVA微服务分布式事务的几种实现方式

    一致性(Consistency) :在分布式系统中所有的数据备份,在同一时刻都保持一致状态,如无法保证状态一致,直接返回错误; 可用性(Availability):在集群中一部分节点故障,也能保证客户端访问系统并得到正确响应,允许一定时间内数据状态不一致; 分区容错性(Partiti

    2024年02月12日
    浏览(51)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包