1.异步概述

 1.1 什么是异步?

  异步是一种编程模式,其中函数或方法的调用不需要等待操作完成即可返回控制权给调用者。这意味着调用者可以在等待操作完成的同时继续执行其他任务。当操作完成后,通常会通过回调、事件通知、Future 或其他机制来告知调用者结果

1.2 相对于同步的优缺点

 优点

  1. 提高性能:异步可以提高程序的并发性和响应能力,特别是在涉及 I/O 操作(如文件读写网络通信)时,可以让 CPU 在等待 I/O 操作完成时去做其他有用的工作
  2. 改善用户体验:在 Web 开发中,异步可以使得应用程序在执行耗时操作时仍然保持界面响应,从而提升用户体验
  3. 资源利用率高:异步模型减少了线程的阻塞时间,从而可以更好地利用系统资源

 缺点

  1. 代码复杂度增加:异步编程通常需要处理回调、事件或使用更复杂的编程模型,这可能会使代码变得难以理解和维护。
  2. 调试难度增加:由于异步代码的执行路径不是线性的,调试起来比同步代码更困难。
  3. 错误处理复杂:异步编程中的错误处理通常更加复杂,因为错误需要通过特殊的机制来捕获和处理。

应用场景

  1. Web 开发:处理 HTTP 请求,尤其是涉及到数据库查询、远程服务调用等耗时操作
  2. 网络编程:网络通信中,读取和发送数据通常是非阻塞的
  3. GUI 应用程序:图形用户界面中的后台任务处理,例如文件上传/下载、数据同步等
  4. 大数据处理批处理任务、数据流处理等场景下,异步处理可以提高处理速度。
  5. 微服务架构服务间通信时,为了减少响应时间,通常采用异步调用

1.3 异步实现增加用户

1.3.1 使用Spring的@Async实现异步

实现流程:

1.配置异步支持;

  在Spring中定义一个TaskExecutor 的Bean异步支持。

@Configuration
@EnableAsync
public class AsyncConfig implements AsyncConfigurer {
    @Bean
    public ThreadPoolTaskExecutor taskExecutor(){
        ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
        taskExecutor.setCorePoolSize(2);
        taskExecutor.setMaxPoolSize(5);
        taskExecutor.setQueueCapacity(10);
        taskExecutor.initialize();
        return taskExecutor;
    }
}

异步配置

2.定义异步方法;

  在服务层定义一个带有 @Async 注解的方法,该方法将负责异步增加用户的功能。

@Async
    public void asyncAddUser(User user) {
        User newUser = new User();
        //根据雪花算法生成分布式ID(定义一个工厂,使用ConcurrentMap做缓存,提高分布式ID生成后查询效率)
        long userId = SnowFlakeFactory.getSnowFlakeFromCache().nextId();
        newUser.setId(userId);
        newUser.setUsername(user.getUsername());
        //利用shiro将username作为盐值加密的Hash的MD5加密算法加密
        String md5Pwd = CommonUtils.encryptPassword(newUser.getUsername(), user.getPassword());
        newUser.setPassword(md5Pwd);
        int i = userMapper.addUser(newUser);
        if (i == 0) throw new TestException("插入用户数据失败!");
        logger.info("插入"+user.getUsername()+"成功!");
    }

异步实现增加用户

 3.结果

1.异步简单使用_监听器

数据库结果

1.异步简单使用_User_02

 1.3.2 使用ApplicationEvent和ApplicationListener实现异步

实现流程:

1.定义事件

  定义一个事件,继承ApplicationEvent添加任何你想要传递给监听器的数据

public class AddUserEvent extends ApplicationEvent {

    private String username;

    private String password;


    public AddUserEvent(Object source, String username, String password) {
        super(source);
        this.username = username;
        this.password = password;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}

t添加用户事件

2.创建一个监听器

  创建一个监听器来监听AddUserEvent事件。当事件被发布时,监听器将执行相应的操作

@Slf4j
public class AddUserEventListener implements ApplicationListener<AddUserEvent> {

    @Autowired(required = false)
    private UserMapper userMapper;

    @Override
    public void onApplicationEvent(AddUserEvent addUserEvent) {
        log.info("接收事件,开始添加用户...");
        if (Objects.isNull(addUserEvent)){
            log.error(TestConstants.UserEvent.EVENT_PARAM_NULL_CODE, TestConstants.UserEvent.EVENT_PARAM_NULL_MSG);
        }
        User newUser = new User();
        String username = addUserEvent.getUsername();
        String password = addUserEvent.getPassword();
        long userId = SnowFlakeFactory.getSnowFlakeFromCache().nextId();
        newUser.setId(userId);
        newUser.setUsername(username);
        //利用shiro将username作为盐值加密的Hash的MD5加密算法加密
        String md5Pwd = CommonUtils.encryptPassword(newUser.getUsername(), password);
        newUser.setPassword(md5Pwd);
        int i = userMapper.addUser(newUser);
        if (i == 0) throw new TestException("插入用户数据失败!");
        log.info("插入"+username+"成功!");
    }
}

新增用户监听器

3.发布事件

  在你的服务层或者控制器中,当一个新用户被创建后,你可以通过ApplicationEventPublisher接口来发布事件

@Service
public class UserServiceImpl implements UserService {

    private static final Logger logger = LoggerFactory.getLogger(UserServiceImpl.class);

    @Resource
    private UserMapper userMapper;

    /**
     * 在服务层或者控制器中,当一个新用户被创建后,通过ApplicationEventPublisher接口来发布事件。
     * @param user
     */
    @Override
    public void asyncAddUser1(User user) {
        //发布添加用户事件
        /**
         * UserService 是触发事件的对象,因此我们将其作为 AddUserEvent 的 source 参数。
         * 优点:任何监听 AddUserEvent 的组件都可以访问到 source,并通过调用 getSource() 方法来获取 UserService 的引用
         */
        AddUserEvent addUserEvent = new AddUserEvent(this, user.getUsername(), user.getPassword());
        publisher.publishEvent(addUserEvent);
        logger.info("添加用户的任务已发布!");
    }
}

发布事件

4.配置一个监听器

  确保监听器被Spring容器管理并且能够接收到事件。可以通过配置类或注解的方式进行注册

@Configuration
public class AsyncConfig implements AsyncConfigurer {
  
    //注册监听器到Spring中并接收事件
    @Bean
    public AddUserEventListener addUserEventListener(){
        return new AddUserEventListener();
    }
}

异步事件配置

 5.结果

1.异步简单使用_添加用户_03

1.异步简单使用_User_04

 

参考链接

这8种java异步实现方式,性能炸裂!