• java异步编程


    Q:为何要采用异步编程

    A:异步编程首先不会节约线程,因为异步操作都会重新开一个线程。异步编程是提高了CPU的使用率,采用同步编程的方式,整个服务器的所有线程大部分都没有在工作,而是在等待。因为线程同步操作

    要等整个事件处理完成才能提交,所以CPU的利用率很低;当采用异步编程,线程不需要等待,减少时间分片的占用,提高系统的吞吐量。在超高请求数量场景下,异步的实现不再需要线程等待执行结果,只需要个位数量的线程,即可实现同步场景大量线程一样的吞吐量。

    举例:

       实现一个转账的微服务 Transfer( accountFrom, accountTo, amount),这个服务有三个参数:

    分别是转出账户、转入账户和转账金额。

      实现过程也比较简单,我们要从账户 A 中转账 100 元到账户 B 中:先从 A 的账户中减去 100 元;再给 B 的账户加上 100 元,转账完成。

      采用同步操作:

      


    Transfer(accountFrom, accountTo, amount) {
    // 先从accountFrom的账户中减去相应的钱数
    Add(accountFrom, -1 * amount)
    // 再把减去的钱数加到accountTo的账户中
    Add(accountTo, amount)
    return OK
    }

      同步操作我们很容易想到的,那同步操作的效率怎么样呢? 

      假设内部add方法执行效率在50ms,那一个线程执行烧水泡茶的时延在100ms,按照这种实现,每个线程每分钟最多处理10个请求,假设我们使用的服务器同时打开的线程数量上限是 10,000,
    可以计算出这台服务器每秒钟可以处理的请求上限是: 10,000 (个线程)* 10(次请求每秒) = 100,000 次每秒。如果请求速度超过这个值,那么请求就不能被马上处理,只能阻塞或者排队,
    这时候 Transfer 服务的响应时延由 100ms 延长到了:排队的等待时延 + 处理时延 (100ms)。也就是说,在大量请求的情况下,我们的微服务的平均响应时延变长了。 这已经到CPU的极限了吗?
    其实不然,如果我们监测一下服务器的各项指标,会发现无论是 CPU、内存,还是网卡流量或者是磁盘的 IO 都空闲的很,多大部分线程都在等待每一个服务返回结果。

      
      

      采用异步操作:

       是在线程模型上由同步顺序调用改为了异步调用和回调的机制。

      接下来,我们来看下,如何用 CompletableFuture 实现的转账服务。

      首先,我们用 CompletableFuture 定义 2 个微服务的接口:

      

    /**
     * 账户服务
     */
    public interface AccountService {
        /**
         * 变更账户金额
         * @param account 账户ID
         * @param amount 增加的金额,负值为减少
         */
        CompletableFuture<Void> add(int account, int amount);
    }
    
    /**
     * 转账服务
     */
    public interface TransferService {
        /**
         * 异步转账服务
         * @param fromAccount 转出账户
         * @param toAccount 转入账户
         * @param amount 转账金额,单位分
         */
        CompletableFuture<Void> transfer(int fromAccount, int toAccount, int amount);
    }
    
    /**
     * 转账服务的实现
     */
    public class TransferServiceImpl implements TransferService {
        @Inject
        private  AccountService accountService; // 使用依赖注入获取账户服务的实例
        @Override
        public CompletableFuture<Void> transfer(int fromAccount, int toAccount, int amount) {
          // 异步调用add方法从fromAccount扣减相应金额
          return accountService.add(fromAccount, -1 * amount)
          // 然后调用add方法给toAccount增加相应金额
          .thenCompose(v -> accountService.add(toAccount, amount));    
        }
    }

      我们先调用一次账户服务 accountService.add() 方法从 fromAccount 扣减响应的金额,因为 add() 方法返回的就是一个 CompletableFeture 对象,可以用 CompletableFeture 的 thenCompose() 方法将下一次调用 accountService.add() 串联起来,实现异步依次调用两次账户服务完整转账。

  • 相关阅读:
    自编码器AutoEncoder,降噪自编码器DAE,稀疏自编码器SAE,变分自编码器VAE 简介
    经验模式分解EMD与集合经验模态分解EEMD
    Adversarial Faces
    网络权重初始化方法 常数初始化、Lecun、Xavier与He Kaiming
    信息熵、交叉熵、KL散度、JS散度、Wasserstein距离
    神经网络前向传播和反向传播公式 详细推导
    Softmax 原理及 Sigmoid和Softmax用于分类的区别
    However, but, yet, while, whereas 表转折的区别; while, whereas区别
    阿里云mysql数据库恢复到本地
    js 14位字符串 转日期
  • 原文地址:https://www.cnblogs.com/volare/p/12287845.html
Copyright © 2020-2023  润新知