Java实现异步的几种方式

打印 上一主题 下一主题

主题 704|帖子 704|积分 2112


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

    和普通线程实现异步区别不大,只是使用Future是要获取执行后的返回值
    1. //创建具有返回值的任务
    2. public class CallableThread implements Callable {
    3.     @Override
    4.     public String call() throws Exception {
    5.         long start = System.currentTimeMillis();
    6.         StopWatch stopWatch = new StopWatch();
    7.         stopWatch.start();
    8.         System.out.println("callable任务开始执行:" + start);
    9.         TimeUnit.SECONDS.sleep(2);
    10.         System.out.println();
    11.         stopWatch.stop();
    12.         System.out.println("stopWatch.prettyPrint------");
    13.         System.out.println(stopWatch.prettyPrint());
    14.         System.out.println("stopWatch.shortSummary------");
    15.         System.out.println(stopWatch.shortSummary());
    16.         System.out.println("stopWatch.getTotalTimeMillis------");
    17.         System.out.println(stopWatch.getTotalTimeMillis());
    18.         return "call执行结束 ";
    19.     }
    20. }
    21. //在业务中进行调用
    22. public String threadFuture(){
    23.     ThreadPoolExecutor threadPool = new ThreadPoolExecutor(3, 3, 5, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(10), new ThreadPoolExecutor.AbortPolicy());
    24.     long start = System.currentTimeMillis();
    25.     CallableThread callableThread = new CallableThread();
    26.     Future<String> submit = threadPool.submit(callableThread);
    27.     try {
    28.         //在获取返回值时会阻塞主线程
    29.         String s = "";
    30.         s = submit.get();
    31.         System.out.println(s);
    32.     } catch (Exception e) {
    33.         System.out.println("线程运行发生错误" + e.getMessage());
    34.         throw new RuntimeException(e);
    35.     }
    36.     long end = System.currentTimeMillis();
    37.     return "接口返回,耗时:" + (end - start);
    38. }
    复制代码
    结果:
  • Spring的@Async异步

    • 使用@Async注解实现异步的前提是需要在启动类上标注@EnableAsync来开启异步配置
    • 配置线程池(@Async默认情况下用的是SimpleAsyncTaskExecutor线程池,该线程池不是真正意义上的线程池,使用此线程池无法实现线程重用,每次调用都会新建一条线程。若系统中不断的创建线程,最终会导致系统占用内存过高,引发OutOfMemoryError错误)
      1. /**
      2. * 线程池配置,可以配置多个线程池
      3. * @Async注解,默认使用系统自定义线程池,可在项目中设置多个线程池,在异步调用的时候,指明需要调用的线程池名称
      4. * 比如:@Async("线程池1")
      5. */
      6. @Configuration
      7. public class ExecutorConfig {
      8.     /**
      9.      * 自定义线程池
      10.      */
      11.     @Bean("myExecutor")
      12.     public Executor taskExecutor(){
      13.         System.out.println("系统最大线程数:" + Runtime.getRuntime().availableProcessors());
      14.         ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
      15.         threadPoolTaskExecutor.setCorePoolSize(8);//核心线程数
      16.         threadPoolTaskExecutor.setMaxPoolSize(16);//最大线程数
      17.         threadPoolTaskExecutor.setQueueCapacity(1000);//配置队列容量
      18.         threadPoolTaskExecutor.setKeepAliveSeconds(60);//空闲线程存活时间
      19.         threadPoolTaskExecutor.setThreadNamePrefix("myExecutor-");//线程名字前缀
      20.         return threadPoolTaskExecutor;
      21.     }
      22. }
      复制代码
    • 编写异步方法的逻辑,异步方法所在的类需要被Spring管理
      1. @Service
      2. public class AsyncServiceImpl implements AsyncService {
      3.     @Override
      4.     @Async("myExecutor")
      5.     public void sendMsg() {
      6.         System.out.println("进入异步方法");
      7.         System.out.println("当前线程名称:" + Thread.currentThread().getName());
      8.         try {
      9.             TimeUnit.SECONDS.sleep(2);
      10.         } catch (InterruptedException e) {
      11.             throw new RuntimeException(e);
      12.         }
      13.         System.out.println("异步方法执行完成");
      14.     }
      15.     /**
      16.      * 具有返回值的异步方法,返回类型为Future,返回时new 一个AsyncResult对象,其中参数为返回的内容
      17.      * @return
      18.      */
      19.     @Override
      20.     @Async("myExecutor")
      21.     public Future<String> sendMsgFuture() {
      22.         System.out.println("进入future异步方法");
      23.         try {
      24.             TimeUnit.SECONDS.sleep(2);
      25.         } catch (InterruptedException e) {
      26.             throw new RuntimeException(e);
      27.         }
      28.         return new AsyncResult<>("future异步方法执行完成");
      29.     }
      30. }
      复制代码
    • 在业务逻辑中调用
      1. @GetMapping("/asyncMethod")
      2. public String asyncMethod(){
      3.     System.out.println("aaa");
      4.     System.out.println("调用异步方法");
      5.     asyncService.sendMsg();
      6.     System.out.println("bbb");
      7.     return "asyncMethod方法返回";
      8. }
      复制代码
      调用没有返回值的异步方法结果:
      1. @GetMapping("/asyncFutureMethod")
      2. public String asyncFutureMethod(){
      3.     System.out.println("aaa");
      4.     Future<String> stringFuture = asyncService.sendMsgFuture();
      5.     System.out.println("bbb");
      6.     try {
      7.     System.out.println(stringFuture.get());//get方法会阻塞主线程
      8.     } catch (Exception e) {
      9.     throw new RuntimeException(e);
      10.     }
      11.     return "asyncfutureMethod方法返回";
      12. }
      复制代码
      调用有返回值的异步方法结果:

  • Spring的ApplicationEvent事件实现异步

    • 定义事件,继承ApplicationEvent类
      1. public class MessageEvent extends ApplicationEvent {
      2.     @Getter
      3.     private String message;
      4.     public MessageEvent(Object source, String message) {
      5.         super(source);
      6.         this.message = message;
      7.     }
      8. }
      复制代码
    • 定义监听器(需要被Spring管理)
      使用@EventListener注解写在方法上定义一个监听器,即事件被触发时执行的方法(默认是同步执行,可以使用@Async注解标注为异步执行),支持多个监听器监听同一个事件。
      1. @Component
      2. public class MessageEventHandler {
      3.     //@Async
      4.     @EventListener
      5.     public void handleLoginEvent(LoginEvent event){
      6.         System.out.println("接受到LoginEvent事件");
      7.         try {
      8.             TimeUnit.SECONDS.sleep(2);
      9.         } catch (InterruptedException e) {
      10.             throw new RuntimeException(e);
      11.         }
      12.         System.out.println(event.getUsername());
      13.         System.out.println("LoginEvent事件处理完成");
      14.     }
      15.     //@Async
      16.     @EventListener
      17.     public void handleMessageEvent(MessageEvent event){
      18.         System.out.println("接受到MessageEvent事件");
      19.         try {
      20.             TimeUnit.SECONDS.sleep(2);
      21.         } catch (InterruptedException e) {
      22.             throw new RuntimeException(e);
      23.         }
      24.         System.out.println(event.getMessage());
      25.         System.out.println("MessageEvent事件处理完成");
      26.     }
      27. }
      复制代码
    • 定义事件发布者(触发事件的)(需要被Spring管理)
      实现ApplicationEventPublisherAware接口
      1. @Component
      2. public class EventPublisher implements ApplicationEventPublisherAware {
      3.     private ApplicationEventPublisher publisher;
      4.     @Override
      5.     public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
      6.         this.publisher = applicationEventPublisher;
      7.     }
      8.     public void publish(ApplicationEvent event){
      9.         if (event instanceof MessageEvent){
      10.             System.out.println("开始发布MessageEvent事件:" + ((MessageEvent) event).getMessage());
      11.         } else if (event instanceof LoginEvent) {
      12.             System.out.println("开始发布LoginEvent事件:" + ((LoginEvent) event).getUsername());
      13.         }
      14.         //发布事件
      15.         publisher.publishEvent(event);
      16.         System.out.println("事件发布结束");
      17.     }
      18. }
      复制代码
    • 业务代码执行时触发事件
      1. @GetMapping("/pubEvent")
      2. public String publishEvent(){
      3.     System.out.println("业务逻辑开始");
      4.     eventPublisher.publish(new MessageEvent(this,"testEvent"));
      5.     System.out.println("业务逻辑结束");
      6.     return "发布成功";
      7. }
      复制代码
      执行结果:
      由控制台打印可以发现现在事件监听器方法的执行是同步的,如果需要异步执行,在监听器方法上加个@Async注解即可,但使用Async注解的前提是在启动类上标注@EnableAsync注解来开启异步配置
      使用@Async注解后执行结果:
      可以看到监听器中的打印在最后了,证明是异步执行的


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

正序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

自由的羽毛

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表